diff --git a/.classpath b/.classpath index 18d70f02..ee32f6f5 100644 --- a/.classpath +++ b/.classpath @@ -1,6 +1,8 @@ - + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..23842834 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/build/ +/dist/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..3a3703f6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +before_script: + - sudo apt-get install ant-optional + - export OPTS="-server -Xmx3072M" + - export JAVA_OPTS="${JAVA_OPTS} ${OPTS}" + - export ANT_OPTS="${ANT_OPTS} ${OPTS}" + - echo $JAVA_OPTS + - echo $ANT_OPTS + +dist: trusty + +language: java + +jdk: + - oraclejdk9 + - oraclejdk8 +# - oraclejdk7 +# - oraclejdk6 + +# - openjdk9 + - openjdk8 + - openjdk7 +# - openjdk6 + +env: + - TEST_SUITE=run_tests +# - TEST_SUITE=mathematics +# - TEST_SUITE=numbers +# - TEST_SUITE=search +# - TEST_SUITE=sequences +# - TEST_SUITE=strings +# - TEST_SUITE=data_structures +# - TEST_SUITE=sorts + +script: "ant $TEST_SUITE" diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..3c921d47 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ + + + +#### By submitting this pull request I confirm I've read and complied with the below requirements. + +- [ ] I have read the [Contribution guidelines](CONTRIBUTING.md) and I am confident that my PR reflects them. +- [ ] I have followed the [coding guidelines](CONTRIBUTING.md#cs) for this project. +- [ ] My code follows the [skeleton code structure](CONTRIBUTING.md#sample). +- [ ] This pull request has a descriptive title. For example, `Added {Algorithm/DS name} [{Language}]`, not `Update README.md` or `Added new code`. diff --git a/README.md b/README.md index 49b9be2c..f4d8bf3a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,220 @@ -java-algorithms-implementation +Java : Algorithms and Data Structure ![alt tag](https://api.travis-ci.org/phishman3579/java-algorithms-implementation.svg?branch=master) ============================== -Algorithms and Data Structures implemented in Java +The algorithms and data structures are implemented in Java. + +This is a collection of algorithms and data structures I've implemented in my academic and professional life. The code isn't optimized but is written to be correct and readable. The algorithms and data structures are tested and, unless noted, believed to be correct. + +## Created by Justin Wetherell + +* For questions use: http://groups.google.com/forum/#!forum/java-algorithms-implementation +* Google: http://code.google.com/p/java-algorithms-implementation +* Github: http://github.com/phishman3579/java-algorithms-implementation +* LinkedIn: http://www.linkedin.com/in/phishman3579 +* E-mail: phishman3579@gmail.com +* Twitter: http://twitter.com/phishman3579 + +## Support me with a donation + +Donate to this project + +# What's been implemented: + +## Table of Contents +- [Data Structures](#data-structures) +- [Mathematics](#mathematics) +- [Numbers](#numbers) +- [Graphs](#graphs) +- [Search](#search) +- [Sequences](#sequences) +- [Sorts](#sorts) +- [String Functions](#string-functions) + +## Data Structures +* [AVL Tree](src/com/jwetherell/algorithms/data_structures/AVLTree.java) +* [B-Tree](src/com/jwetherell/algorithms/data_structures/BTree.java) +* [Binary Heap (backed by an array or a tree)](src/com/jwetherell/algorithms/data_structures/BinaryHeap.java) +* [Binary Search Tree](src/com/jwetherell/algorithms/data_structures/BinarySearchTree.java) +* [Compact Suffix Trie (backed by a Patricia Trie)](src/com/jwetherell/algorithms/data_structures/CompactSuffixTrie.java) +* [Disjoint Set](src/com/jwetherell/algorithms/data_structures/DisjointSet.java) +* [Fenwick Tree {Binary Indexed Tree (BIT)}](src/com/jwetherell/algorithms/data_structures/FenwickTree.java) +* [Graph](src/com/jwetherell/algorithms/data_structures/Graph.java) + + Undirected + + Directed (Digraph) +* [Hash Array Mapped Trie (HAMT)](src/com/jwetherell/algorithms/data_structures/HashArrayMappedTrie.java) +* [Hash Map (associative array)](src/com/jwetherell/algorithms/data_structures/HashMap.java) +* [Interval Tree](src/com/jwetherell/algorithms/data_structures/IntervalTree.java) +* [Implicit Key Treap](src/com/jwetherell/algorithms/data_structures/ImplicitKeyTreap.java) +* [KD Tree (k-dimensional tree or k-d tree)](src/com/jwetherell/algorithms/data_structures/KdTree.java) +* [List [backed by an array or a linked list]](src/com/jwetherell/algorithms/data_structures/List.java) +* [LCP Array (Longest Common Prefix) [backed by a Suffix Array]](src/com/jwetherell/algorithms/data_structures/LCPArray.java) +* [Matrix](src/com/jwetherell/algorithms/data_structures/Matrix.java) +* [Patricia Trie](src/com/jwetherell/algorithms/data_structures/PatriciaTrie.java) +* [Quad-Tree (Point-Region or MX-CIF)](src/com/jwetherell/algorithms/data_structures/QuadTree.java) +* [Queue [backed by an array or a linked list]](src/com/jwetherell/algorithms/data_structures/Queue.java) +* [Radix Trie (associative array) [backed by a Patricia Trie]](src/com/jwetherell/algorithms/data_structures/RadixTrie.java) +* [Red-Black Tree](src/com/jwetherell/algorithms/data_structures/RedBlackTree.java) +* [Segment Tree](src/com/jwetherell/algorithms/data_structures/SegmentTree.java) +* [Skip List](src/com/jwetherell/algorithms/data_structures/SkipList.java) +* [Splay Tree](src/com/jwetherell/algorithms/data_structures/SplayTree.java) +* [Stack [backed by an array or a linked list]](src/com/jwetherell/algorithms/data_structures/Stack.java) +* [Suffix Array](src/com/jwetherell/algorithms/data_structures/SuffixArray.java) +* [Suffix Tree (Ukkonen's algorithm)](src/com/jwetherell/algorithms/data_structures/SuffixTree.java) +* [Suffix Trie [backed by a Trie]](src/com/jwetherell/algorithms/data_structures/SuffixTrie.java) +* [Ternary Search Tree](src/com/jwetherell/algorithms/data_structures/TernarySearchTree.java) +* [Treap](src/com/jwetherell/algorithms/data_structures/Treap.java) +* [Tree](src/com/jwetherell/algorithms/data_structures/Tree.java) +* [Tree Map (associative array) [backed by an AVL Tree]](src/com/jwetherell/algorithms/data_structures/TreeMap.java) +* [Trie](src/com/jwetherell/algorithms/data_structures/Trie.java) +* [Trie Map (associative array) [backed by a Trie]](src/com/jwetherell/algorithms/data_structures/TrieMap.java) + +## Mathematics +* [Distance](src/com/jwetherell/algorithms/mathematics/Distance.java) + + chebyshev + + euclidean +* [Division](src/com/jwetherell/algorithms/mathematics/Division.java) + + using a loop + + using recursion + + using shifts and multiplication + + using only shifts + + using logarithm +* [Multiplication](src/com/jwetherell/algorithms/mathematics/Multiplication.java) + + using a loop + + using recursion + + using only shifts + + using logarithms + + [Fast Fourier Transform](src/com/jwetherell/algorithms/mathematics/FastFourierTransform.java) +* [Exponentiation](src/com/jwetherell/algorithms/mathematics/Exponentiation.java) + + recursive exponentiation + + fast recursive exponentiation + + fast modular recursive exponentiation +* [Primes](src/com/jwetherell/algorithms/mathematics/Primes.java) + + is prime + + prime factorization + + sieve of eratosthenes + + Miller-Rabin test + + [Co-Primes (relatively prime, mutually prime)](src/com/jwetherell/algorithms/mathematics/Coprimes.java) + + [Greatest Common Divisor](src/com/jwetherell/algorithms/mathematics/GreatestCommonDivisor.java) + - using Euclid's algorithm + - using recursion +* [Permutations](src/com/jwetherell/algorithms/mathematics/Permutations.java) + + strings + + numbers +* [Modular arithmetic](src/com/jwetherell/algorithms/mathematics/Modular.java) + + add + + subtract + + multiply + + divide + + power +* [Knapsack](src/com/jwetherell/algorithms/mathematics/Knapsack.java) +* [Ramer Douglas Peucker](src/com/jwetherell/algorithms/mathematics/RamerDouglasPeucker.java) + +## Numbers +* [Integers](src/com/jwetherell/algorithms/numbers/Integers.java) + + to binary String + - using divide and modulus + - using right shift and modulus + - using BigDecimal + - using divide and double + + is a power of 2 + - using a loop + - using recursion + - using logarithm + - using bits + + to English (e.g. 1 would return "one") +* [Longs](src/com/jwetherell/algorithms/numbers/Longs.java) + + to binary String + - using divide and modulus + - using right shift and modulus + - using BigDecimal +* [Complex](src/com/jwetherell/algorithms/numbers/Complex.java) + + addition + + subtraction + + multiplication + + absolute value + + polar value + +## Graphs +* Find shortest path(s) in a Graph from a starting Vertex + - [Dijkstra's algorithm (non-negative weight graphs)](src/com/jwetherell/algorithms/graph/Dijkstra.java) + - [Bellman-Ford algorithm (negative and positive weight graphs)](src/com/jwetherell/algorithms/graph/BellmanFord.java) +* Find minimum spanning tree + - [Prim's (undirected graphs)](src/com/jwetherell/algorithms/graph/Prim.java) + - [Kruskal's (undirected graphs)](src/com/jwetherell/algorithms/graph/Kruskal.java) +* Find all pairs shortest path + - [Johnsons's algorithm (negative and positive weight graphs)](src/com/jwetherell/algorithms/graph/Johnsons.java) + - [Floyd-Warshall (negative and positive weight graphs)](src/com/jwetherell/algorithms/graph/FloydWarshall.java) +* [Cycle detection](src/com/jwetherell/algorithms/graph/CycleDetection.java) + - Depth first search while keeping track of visited Verticies + - [Connected Components](src/com/jwetherell/algorithms/graph/ConnectedComponents.java) +* [Topological sort](src/com/jwetherell/algorithms/graph/TopologicalSort.java) +* [A* path finding algorithm](src/com/jwetherell/algorithms/graph/AStar.java) +* Maximum flow + - [Push-Relabel](src/com/jwetherell/algorithms/graph/PushRelabel.java) +* Graph Traversal + - [Depth First Traversal](src/com/jwetherell/algorithms/graph/DepthFirstTraversal.java) + - [Breadth First Traversal](src/com/jwetherell/algorithms/graph/BreadthFirstTraversal.java) +* [Edmonds Karp](src/com/jwetherell/algorithms/graph/EdmondsKarp.java) +* Matching + - [Turbo Matching](src/com/jwetherell/algorithms/graph/TurboMatching.java) +* [Lowest common ancestor in tree](src/com/jwetherell/algorithms/data_structures/Tree.java) + + +## Search +* Get index of value in array + + [Linear](src/com/jwetherell/algorithms/search/LinearSearch.java) + + [Quickselect](src/com/jwetherell/algorithms/search/QuickSelect.java) + + [Binary [sorted array input only]](src/com/jwetherell/algorithms/search/BinarySearch.java) + + [Lower bound [sorted array input only]](src/com/jwetherell/algorithms/search/LowerBound.java) + + [Upper bound [sorted array input only]](src/com/jwetherell/algorithms/search/UpperBound.java) + + Optimized binary (binary until a threashold then linear) [sorted array input only] + + [Interpolation [sorted array input only]](src/com/jwetherell/algorithms/search/InterpolationSearch.java) + +## Sequences +* [Find longest common subsequence (dynamic programming)](src/com/jwetherell/algorithms/sequence/LongestCommonSubsequence.java) +* [Find longest increasing subsequence (dynamic programming)](src/com/jwetherell/algorithms/sequence/LongestIncreasingSubsequence.java) +* [Find number of times a subsequence occurs in a sequence (dynamic programming)](src/com/jwetherell/algorithms/sequence/SubsequenceCounter.java) +* [Find i-th element in a Fibonacci sequence](src/com/jwetherell/algorithms/sequence/FibonacciSequence.java) + + using a loop + + using recursion + + using matrix multiplication + + using Binet's formula +* [Find total of all elements in a sequence(Arithmetic Progression)](src/com/jwetherell/algorithms/sequence/ArithmeticProgression.java) + + using a loop + + using Triangular numbers +* [Largest sum of contiguous subarray (Kadane's algorithm)](src/com/jwetherell/algorithms/sequence/LargestSumContiguousSubarray.java) +* [Longest palin­dromic sub­se­quence (dynamic programming)](src/com/jwetherell/algorithms/sequence/LongestPalindromicSubsequence.java) + +## Sorts +* [American Flag Sort](src/com/jwetherell/algorithms/sorts/AmericanFlagSort.java) +* [Bubble Sort](src/com/jwetherell/algorithms/sorts/BubbleSort.java) +* [Counting Sort (Integers only)](src/com/jwetherell/algorithms/sorts/CountingSort.java) +* [Heap Sort](src/com/jwetherell/algorithms/sorts/HeapSort.java) +* [Insertion Sort](src/com/jwetherell/algorithms/sorts/InsertionSort.java) +* [Merge Sort](src/com/jwetherell/algorithms/sorts/MergeSort.java) +* [Quick Sort](src/com/jwetherell/algorithms/sorts/QuickSort.java) +* [Radix Sort (Integers only)](src/com/jwetherell/algorithms/sorts/RadixSort.java) +* [Shell's Sort](src/com/jwetherell/algorithms/sorts/ShellSort.java) + +## String Functions +### [String Functions](src/com/jwetherell/algorithms/strings/StringFunctions.java) +* Reverse characters in a string + + using additional storage (a String or StringBuilder) + + using in-place swaps + + using in-place XOR +* Reverse words in a string + + using char swaps and additional storage (a StringBuilder) + + using StringTokenizer and additional (a String) + + using split() method and additional storage (a StringBuilder and String[]) + + using in-place swaps +* Is Palindrome + + using additional storage (a StringBuilder) + + using in-place symetric element compares +* Subsets of characters in a String +* Edit (Levenshtein) Distance of two Strings (Recursive, Iterative) +### [Manacher's algorithm (Find the longest Palindrome)](src/com/jwetherell/algorithms/strings/Manacher.java) +### [KMP (Knuth–Morris–Pratt) Algorithm - Length of maximal prefix-suffix for each prefix](src/com/jwetherell/algorithms/strings/KnuthMorrisPratt.java) +### [String rotations](src/com/jwetherell/algorithms/strings/Rotation.java) + + Find in lexicographically minimal string rotation + + Find in lexicographically maximal string rotation + diff --git a/build.xml b/build.xml new file mode 100644 index 00000000..b926ead8 --- /dev/null +++ b/build.xml @@ -0,0 +1,148 @@ + + + java-algorithms-implementation build file + + + + + + + + + + + + + + + + + + + + + + + + + + CLASSPATH='${env_vars.CLASSPATH}' + JAVA_HOME='${env_vars.JAVA_HOME}' + JAVA_OPTS='${env_vars.JAVA_OPTS}' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/junit-4_4.3.1.jar b/lib/junit-4_4.3.1.jar new file mode 100644 index 00000000..5d9ccff6 Binary files /dev/null and b/lib/junit-4_4.3.1.jar differ diff --git a/src/com/jwetherell/algorithms/DataStructures.java b/src/com/jwetherell/algorithms/DataStructures.java deleted file mode 100644 index 3017dcad..00000000 --- a/src/com/jwetherell/algorithms/DataStructures.java +++ /dev/null @@ -1,4098 +0,0 @@ -package com.jwetherell.algorithms; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Formatter; -import java.util.Iterator; -import java.util.ListIterator; -import java.util.Locale; -import java.util.Map; -import java.util.NavigableSet; -import java.util.NoSuchElementException; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.ConcurrentSkipListSet; - -import com.jwetherell.algorithms.data_structures.AVLTree; -import com.jwetherell.algorithms.data_structures.BTree; -import com.jwetherell.algorithms.data_structures.BinarySearchTree; -import com.jwetherell.algorithms.data_structures.BinaryHeap; -import com.jwetherell.algorithms.data_structures.CompactSuffixTrie; -import com.jwetherell.algorithms.data_structures.Graph.Edge; -import com.jwetherell.algorithms.data_structures.Graph.Vertex; -import com.jwetherell.algorithms.data_structures.Graph; -import com.jwetherell.algorithms.data_structures.HashArrayMappedTrie; -import com.jwetherell.algorithms.data_structures.HashMap; -import com.jwetherell.algorithms.data_structures.IHeap; -import com.jwetherell.algorithms.data_structures.IList; -import com.jwetherell.algorithms.data_structures.IMap; -import com.jwetherell.algorithms.data_structures.IQueue; -import com.jwetherell.algorithms.data_structures.ISet; -import com.jwetherell.algorithms.data_structures.IStack; -import com.jwetherell.algorithms.data_structures.ITree; -import com.jwetherell.algorithms.data_structures.IntervalTree; -import com.jwetherell.algorithms.data_structures.KdTree; -import com.jwetherell.algorithms.data_structures.PatriciaTrie; -import com.jwetherell.algorithms.data_structures.QuadTree; -import com.jwetherell.algorithms.data_structures.RadixTrie; -import com.jwetherell.algorithms.data_structures.RedBlackTree; -import com.jwetherell.algorithms.data_structures.SegmentTree; -import com.jwetherell.algorithms.data_structures.SegmentTree.DynamicSegmentTree; -import com.jwetherell.algorithms.data_structures.SegmentTree.FlatSegmentTree; -import com.jwetherell.algorithms.data_structures.SkipListMap; -import com.jwetherell.algorithms.data_structures.SuffixTree; -import com.jwetherell.algorithms.data_structures.TreeMap; -import com.jwetherell.algorithms.data_structures.TrieMap; -import com.jwetherell.algorithms.data_structures.List; -import com.jwetherell.algorithms.data_structures.Matrix; -import com.jwetherell.algorithms.data_structures.Queue; -import com.jwetherell.algorithms.data_structures.SkipList; -import com.jwetherell.algorithms.data_structures.SplayTree; -import com.jwetherell.algorithms.data_structures.Stack; -import com.jwetherell.algorithms.data_structures.SuffixTrie; -import com.jwetherell.algorithms.data_structures.Treap; -import com.jwetherell.algorithms.data_structures.Trie; -import com.jwetherell.algorithms.graph.BellmanFord; -import com.jwetherell.algorithms.graph.CycleDetection; -import com.jwetherell.algorithms.graph.Dijkstra; -import com.jwetherell.algorithms.graph.FloydWarshall; -import com.jwetherell.algorithms.graph.Johnson; -import com.jwetherell.algorithms.graph.Prim; -import com.jwetherell.algorithms.graph.TopologicalSort; - -@SuppressWarnings("unchecked") -public class DataStructures { - - private static final int NUMBER_OF_TESTS = 3; - private static final Random RANDOM = new Random(); - private static final int ARRAY_SIZE = 1000; - private static final int RANDOM_SIZE = 1000 * ARRAY_SIZE; - private static final Integer INVALID = RANDOM_SIZE + 10; - private static final DecimalFormat FORMAT = new DecimalFormat("0.##"); - - private static Integer[] unsorted = null; - private static Integer[] sorted = null; - private static String string = null; - - private static int debug = 1; // Debug level. 0=None, 1=Time and Memory (if enabled), 2=Time, Memory, data structure debug - private static boolean debugTime = true; // How much time to: add all, remove all, add all items in reverse order, remove all - private static boolean debugMemory = true; // How much memory is used by the data structure - private static boolean validateStructure = true; // Is the data structure valid (passed invariants) and proper size - private static boolean validateContents = true; // Was the item added/removed really added/removed from the structure - private static boolean validateIterator = true; // Does the iterator(s) work - - private static final int TESTS = 38; // Max number of dynamic data structures to test - private static final String[] testNames = new String[TESTS]; // Array to hold the test names - private static final long[][] testResults = new long[TESTS][]; // Array to hold the test results - private static int testIndex = 0; // Index into the tests - private static int testNumber = 0; // Number of aggregate tests which have been run - - private static enum Type {Integer, String}; - - public static void main(String[] args) { - System.out.println("Starting tests."); - boolean passed = false; - for (int i = 0; i < NUMBER_OF_TESTS; i++) { - try { - passed = runTests(); - } catch (NullPointerException e) { - System.err.println(string); - throw e; - } - if (!passed) break; - } - if (passed) System.out.println("Tests finished. All passed."); - else System.err.println("Tests finished. Detected a failure."); - } - - private static void generateData() { - System.out.println("Generating data."); - StringBuilder builder = new StringBuilder(); - builder.append("Array="); - unsorted = new Integer[ARRAY_SIZE]; - java.util.Set set = new java.util.HashSet(); - for (int i = 0; i < ARRAY_SIZE; i++) { - Integer j = RANDOM.nextInt(RANDOM_SIZE); - // Make sure there are no duplicates - boolean found = true; - while (found) { - if (set.contains(j)) { - j = RANDOM.nextInt(RANDOM_SIZE); - } else { - unsorted[i] = j; - set.add(j); - found = false; - } - } - unsorted[i] = j; - if (i!=ARRAY_SIZE-1) builder.append(j).append(','); - } - set.clear(); - set = null; - builder.append('\n'); - string = builder.toString(); - if (debug > 1) System.out.println(string); - - sorted = Arrays.copyOf(unsorted, unsorted.length); - Arrays.sort(sorted); - - System.out.println("Generated data."); - } - - private static boolean runTests() { - testIndex = 0; - testNumber++; - - generateData(); - - boolean passed = true; - - // Trees - - passed = testJavaRedBlackIntegerTree(); - if (!passed) { - System.err.println("Java Red-Black [Integer] failed."); - return false; - } - - passed = testRedBlackTree(); - if (!passed) { - System.err.println("Red-Black Tree failed."); - return false; - } - - passed = testAVLTree(); - if (!passed) { - System.err.println("AVL Tree failed."); - return false; - } - - passed = testSplayTree(); - if (!passed) { - System.err.println("Splay Tree failed."); - return false; - } - - passed = testBTree(); - if (!passed) { - System.err.println("B-Tree failed."); - return false; - } - - passed = testTreap(); - if (!passed) { - System.err.println("Treap failed."); - return false; - } - - passed = testBST(); - if (!passed) { - System.err.println("BST failed."); - return false; - } - - passed = testJavaRedBlackStringTree(); - if (!passed) { - System.err.println("Java Red-Black [String] failed."); - return false; - } - - passed = testTrie(); - if (!passed) { - System.err.println("Trie failed."); - return false; - } - - passed = testPatriciaTrie(); - if (!passed) { - System.err.println("Patricia Trie failed."); - return false; - } - - // Sets - - passed = testJavaSkipList(); - if (!passed) { - System.err.println("Java's Skip List failed."); - return false; - } - - passed = testSkipList(); - if (!passed) { - System.err.println("Skip List failed."); - return false; - } - - // Heaps - - passed = testJavaMinHeap(); - if (!passed) { - System.err.println("Java Min-Heap failed."); - return false; - } - - passed = testMinHeap(); - if (!passed) { - System.err.println("Min-Heap failed."); - return false; - } - - passed = testJavaMaxHeap(); - if (!passed) { - System.err.println("Java Max-Heap failed."); - return false; - } - - passed = testMaxHeap(); - if (!passed) { - System.err.println("Max-Heap failed."); - return false; - } - - // Lists - - passed = testJavaArrayList(); - if (!passed) { - System.err.println("Java List failed."); - return false; - } - - passed = testArrayList(); - if (!passed) { - System.err.println("List failed."); - return false; - } - - passed = testJavaLinkedList(); - if (!passed) { - System.err.println("Java List failed."); - return false; - } - - passed = testLinkedList(); - if (!passed) { - System.err.println("List failed."); - return false; - } - - // Queues - - passed = testJavaArrayQueue(); - if (!passed) { - System.err.println("Java Queue failed."); - return false; - } - - passed = testArrayQueue(); - if (!passed) { - System.err.println("Queue failed."); - return false; - } - - passed = testJavaLinkedQueue(); - if (!passed) { - System.err.println("Java Queue failed."); - return false; - } - - passed = testLinkedQueue(); - if (!passed) { - System.err.println("Queue failed."); - return false; - } - - // Stacks - - passed = testJavaStack(); - if (!passed) { - System.err.println("Java Stack failed."); - return false; - } - - passed = testArrayStack(); - if (!passed) { - System.err.println("Stack failed."); - return false; - } - - passed = testLinkedStack(); - if (!passed) { - System.err.println("Stack failed."); - return false; - } - - // Maps - - passed = testJavaHashMap(); - if (!passed) { - System.err.println("Java Hash Map failed."); - return false; - } - - passed = testHashMap(); - if (!passed) { - System.err.println("Hash Map failed."); - return false; - } - - passed = testJavaTreeMap(); - if (!passed) { - System.err.println("Java Tree Map failed."); - return false; - } - - passed = testTreeMap(); - if (!passed) { - System.err.println("Tree Map failed."); - return false; - } - - passed = testTrieMap(); - if (!passed) { - System.err.println("Trie Map failed."); - return false; - } - - passed = testRadixTrie(); - if (!passed) { - System.err.println("Radix Trie failed."); - return false; - } - - passed = testJavaSkipListMap(); - if (!passed) { - System.err.println("Java's Skip List Map failed."); - return false; - } - - passed = testSkipListMap(); - if (!passed) { - System.err.println("Skip List Map failed."); - return false; - } - - passed = testHAMT(); - if (!passed) { - System.err.println("HAMT failed."); - return false; - } - - if (debugTime && debugMemory) { - String results = getTestResults(testNumber, testNames, testResults); - System.out.println(results); - } - - // MY STATIC DATA STRUCTURES - - passed = testCompactSuffixTrie(); - if (!passed) { - System.err.println("Compact Suffix Trie failed."); - return false; - } - - passed = testGraph(); - if (!passed) { - System.err.println("Graph failed."); - return false; - } - - passed = testIntervalTree(); - if (!passed) { - System.err.println("Interval Tree failed."); - return false; - } - - passed = testKdTree(); - if (!passed) { - System.err.println("k-d Tree Tree failed."); - return false; - } - - passed = testMatrix(); - if (!passed) { - System.err.println("Matrix failed."); - return false; - } - - passed = testQuadTree(); - if (!passed) { - System.err.println("QuadTree failed."); - return false; - } - - passed = testSegmentTree(); - if (!passed) { - System.err.println("Segment Tree failed."); - return false; - } - - passed = testSuffixTree(); - if (!passed) { - System.err.println("Suffix Tree failed."); - return false; - } - - passed = testSuffixTrie(); - if (!passed) { - System.err.println("Suffix Trie failed."); - return false; - } - - return true; - } - - private static void handleError(Object obj) { - System.err.println(string); - System.err.println(obj.toString()); - throw new RuntimeException(); - } - - private static boolean testAVLTree() { - String bstName = "AVL Tree"; - BinarySearchTree bst = new AVLTree(); - Collection bstCollection = bst.toCollection(); - - if((validateStructure||validateContents) && !testTree(bst,Type.Integer,bstName)) return false; - if(!testJavaCollection(bstCollection,Type.Integer,bstName)) return false; - return true; - } - - private static boolean testBTree() { - String bstName = "B-Tree"; - BTree bst = new BTree(2); - Collection bstCollection = bst.toCollection(); - - if((validateStructure||validateContents) && !testTree(bst,Type.Integer,bstName)) return false; - if(!testJavaCollection(bstCollection,Type.Integer,bstName)) return false; - return true; - } - - private static boolean testBST() { - String bstName = "BST"; - BinarySearchTree bst = new BinarySearchTree(); - Collection bstCollection = bst.toCollection(); - - if((validateStructure||validateContents) && !testTree(bst,Type.Integer,bstName)) return false; - if(!testJavaCollection(bstCollection,Type.Integer,bstName)) return false; - return true; - } - - private static boolean testCompactSuffixTrie() { - if (debug > 1) System.out.println("Compact Suffix Trie."); - String bookkeeper = "bookkeeper"; - CompactSuffixTrie trie = new CompactSuffixTrie(bookkeeper); - if (debug > 1) System.out.println(trie.toString()); - if (debug > 1) System.out.println(trie.getSuffixes()); - - boolean exists = trie.doesSubStringExist(bookkeeper); - if (!exists) { - System.err.println("YIKES!! " + bookkeeper + " doesn't exists."); - handleError(trie); - return false; - } - - String failed = "booker"; - exists = trie.doesSubStringExist(failed); - if (exists) { - System.err.println("YIKES!! " + failed + " exists."); - handleError(trie); - return false; - } - - String pass = "kkee"; - exists = trie.doesSubStringExist(pass); - if (!exists) { - System.err.println("YIKES!! " + pass + " doesn't exists."); - handleError(trie); - return false; - } - - if (debug > 1) System.out.println(); - - return true; - } - - private static boolean testGraph() { - { // UNDIRECTED GRAPH - if (debug > 1) System.out.println("Undirected Graph."); - java.util.List> verticies = new ArrayList>(); - Graph.Vertex v1 = new Graph.Vertex(1); - verticies.add(v1); - Graph.Vertex v2 = new Graph.Vertex(2); - verticies.add(v2); - Graph.Vertex v3 = new Graph.Vertex(3); - verticies.add(v3); - Graph.Vertex v4 = new Graph.Vertex(4); - verticies.add(v4); - Graph.Vertex v5 = new Graph.Vertex(5); - verticies.add(v5); - Graph.Vertex v6 = new Graph.Vertex(6); - verticies.add(v6); - - java.util.List> edges = new ArrayList>(); - Graph.Edge e1_2 = new Graph.Edge(7, v1, v2); - edges.add(e1_2); - Graph.Edge e1_3 = new Graph.Edge(9, v1, v3); - edges.add(e1_3); - Graph.Edge e1_6 = new Graph.Edge(14, v1, v6); - edges.add(e1_6); - Graph.Edge e2_3 = new Graph.Edge(10, v2, v3); - edges.add(e2_3); - Graph.Edge e2_4 = new Graph.Edge(15, v2, v4); - edges.add(e2_4); - Graph.Edge e3_4 = new Graph.Edge(11, v3, v4); - edges.add(e3_4); - Graph.Edge e3_6 = new Graph.Edge(2, v3, v6); - edges.add(e3_6); - Graph.Edge e5_6 = new Graph.Edge(9, v5, v6); - edges.add(e5_6); - Graph.Edge e4_5 = new Graph.Edge(6, v4, v5); - edges.add(e4_5); - - Graph undirected = new Graph(verticies, edges); - if (debug > 1) System.out.println(undirected.toString()); - - Graph.Vertex start = v1; - if (debug > 1) System.out.println("Dijstra's shortest paths of the undirected graph from " + start.getValue()); - Map, Graph.CostPathPair> map1 = Dijkstra.getShortestPaths(undirected, start); - if (debug > 1) System.out.println(getPathMapString(start, map1)); - - Graph.Vertex end = v5; - if (debug > 1) System.out.println("Dijstra's shortest path of the undirected graph from " + start.getValue() + " to " + end.getValue()); - Graph.CostPathPair pair1 = Dijkstra.getShortestPath(undirected, start, end); - if (debug > 1) { - if (pair1 != null) System.out.println(pair1.toString()); - else System.out.println("No path from " + start.getValue() + " to " + end.getValue()); - } - - start = v1; - if (debug > 1) System.out.println("Bellman-Ford's shortest paths of the undirected graph from " + start.getValue()); - Map, Graph.CostPathPair> map2 = BellmanFord.getShortestPaths(undirected, start); - if (debug > 1) System.out.println(getPathMapString(start, map2)); - - end = v5; - if (debug > 1) System.out.println("Bellman-Ford's shortest path of the undirected graph from " + start.getValue() + " to " + end.getValue()); - Graph.CostPathPair pair2 = BellmanFord.getShortestPath(undirected, start, end); - if (debug > 1) { - if (pair2 != null) System.out.println(pair2.toString()); - else System.out.println("No path from " + start.getValue() + " to " + end.getValue()); - } - - // MST - - if (debug > 1) System.out.println("Prim's minimum spanning tree of the undirected graph from " + start.getValue()); - Graph.CostPathPair pair = Prim.getMinimumSpanningTree(undirected, start); - if (debug > 1) System.out.println(pair.toString()); - - // Prim on a graph with cycles - java.util.List> cyclicVerticies = new ArrayList>(); - Graph.Vertex cv1 = new Graph.Vertex(1); - cyclicVerticies.add(cv1); - Graph.Vertex cv2 = new Graph.Vertex(2); - cyclicVerticies.add(cv2); - Graph.Vertex cv3 = new Graph.Vertex(3); - cyclicVerticies.add(cv3); - Graph.Vertex cv4 = new Graph.Vertex(4); - cyclicVerticies.add(cv4); - Graph.Vertex cv5 = new Graph.Vertex(5); - cyclicVerticies.add(cv5); - - java.util.List> cyclicEdges = new ArrayList>(); - Graph.Edge ce1_2 = new Graph.Edge(3, cv1, cv2); - cyclicEdges.add(ce1_2); - Graph.Edge ce2_3 = new Graph.Edge(2, cv2, cv3); - cyclicEdges.add(ce2_3); - Graph.Edge ce3_4 = new Graph.Edge(4, cv3, cv4); - cyclicEdges.add(ce3_4); - Graph.Edge ce4_1 = new Graph.Edge(1, cv4, cv1); - cyclicEdges.add(ce4_1); - Graph.Edge ce4_5 = new Graph.Edge(1, cv4, cv5); - cyclicEdges.add(ce4_5); - - Graph cyclicUndirected = new Graph(cyclicVerticies, cyclicEdges); - if (debug > 1) System.out.println(cyclicUndirected.toString()); - - start = cv1; - if (debug > 1) System.out.println("Prim's minimum spanning tree of a cyclic undirected graph from " + start.getValue()); - Graph.CostPathPair cyclicPair = Prim.getMinimumSpanningTree(cyclicUndirected, start); - if (debug > 1) System.out.println(cyclicPair.toString()); - - if (debug > 1) System.out.println(); - } - - { // DIRECTED GRAPH - if (debug > 1) System.out.println("Directed Graph."); - java.util.List> verticies = new ArrayList>(); - Graph.Vertex v1 = new Graph.Vertex(1); - verticies.add(v1); - Graph.Vertex v2 = new Graph.Vertex(2); - verticies.add(v2); - Graph.Vertex v3 = new Graph.Vertex(3); - verticies.add(v3); - Graph.Vertex v4 = new Graph.Vertex(4); - verticies.add(v4); - Graph.Vertex v5 = new Graph.Vertex(5); - verticies.add(v5); - Graph.Vertex v6 = new Graph.Vertex(6); - verticies.add(v6); - Graph.Vertex v7 = new Graph.Vertex(7); - verticies.add(v7); - - java.util.List> edges = new ArrayList>(); - Graph.Edge e1_2 = new Graph.Edge(7, v1, v2); - edges.add(e1_2); - Graph.Edge e1_3 = new Graph.Edge(9, v1, v3); - edges.add(e1_3); - Graph.Edge e1_6 = new Graph.Edge(14, v1, v6); - edges.add(e1_6); - Graph.Edge e2_3 = new Graph.Edge(10, v2, v3); - edges.add(e2_3); - Graph.Edge e2_4 = new Graph.Edge(15, v2, v4); - edges.add(e2_4); - Graph.Edge e3_4 = new Graph.Edge(11, v3, v4); - edges.add(e3_4); - Graph.Edge e3_6 = new Graph.Edge(2, v3, v6); - edges.add(e3_6); - Graph.Edge e6_5 = new Graph.Edge(9, v6, v5); - edges.add(e6_5); - Graph.Edge e4_5 = new Graph.Edge(6, v4, v5); - edges.add(e4_5); - Graph.Edge e4_7 = new Graph.Edge(16, v4, v7); - edges.add(e4_7); - - Graph directed = new Graph(Graph.TYPE.DIRECTED, verticies, edges); - if (debug > 1) System.out.println(directed.toString()); - - Graph.Vertex start = v1; - if (debug > 1) System.out.println("Dijstra's shortest paths of the directed graph from " + start.getValue()); - Map, Graph.CostPathPair> map = Dijkstra.getShortestPaths(directed, start); - if (debug > 1) System.out.println(getPathMapString(start, map)); - - Graph.Vertex end = v5; - if (debug > 1) System.out.println("Dijstra's shortest path of the directed graph from " + start.getValue() + " to " + end.getValue()); - Graph.CostPathPair pair = Dijkstra.getShortestPath(directed, start, end); - if (debug > 1) { - if (pair != null) System.out.println(pair.toString()); - else System.out.println("No path from " + start.getValue() + " to " + end.getValue()); - } - - start = v1; - if (debug > 1) System.out.println("Bellman-Ford's shortest paths of the undirected graph from " + start.getValue()); - Map, Graph.CostPathPair> map2 = BellmanFord.getShortestPaths(directed, start); - if (debug > 1) System.out.println(getPathMapString(start, map2)); - - end = v5; - if (debug > 1) System.out.println("Bellman-Ford's shortest path of the undirected graph from " + start.getValue() + " to " + end.getValue()); - Graph.CostPathPair pair2 = BellmanFord.getShortestPath(directed, start, end); - if (debug > 1) { - if (pair2 != null) System.out.println(pair2.toString()); - else System.out.println("No path from " + start.getValue() + " to " + end.getValue()); - } - - if (debug > 1) System.out.println(); - } - - { // DIRECTED GRAPH (WITH NEGATIVE WEIGHTS) - if (debug > 1) System.out.println("Undirected Graph with Negative Weights."); - java.util.List> verticies = new ArrayList>(); - Graph.Vertex v1 = new Graph.Vertex(1); - verticies.add(v1); - Graph.Vertex v2 = new Graph.Vertex(2); - verticies.add(v2); - Graph.Vertex v3 = new Graph.Vertex(3); - verticies.add(v3); - Graph.Vertex v4 = new Graph.Vertex(4); - verticies.add(v4); - - java.util.List> edges = new ArrayList>(); - Graph.Edge e1_4 = new Graph.Edge(2, v1, v4); - edges.add(e1_4); - Graph.Edge e2_1 = new Graph.Edge(6, v2, v1); - edges.add(e2_1); - Graph.Edge e2_3 = new Graph.Edge(3, v2, v3); - edges.add(e2_3); - Graph.Edge e3_1 = new Graph.Edge(4, v3, v1); - edges.add(e3_1); - Graph.Edge e3_4 = new Graph.Edge(5, v3, v4); - edges.add(e3_4); - Graph.Edge e4_2 = new Graph.Edge(-7, v4, v2); - edges.add(e4_2); - Graph.Edge e4_3 = new Graph.Edge(-3, v4, v3); - edges.add(e4_3); - - Graph directed = new Graph(Graph.TYPE.DIRECTED, verticies, edges); - if (debug > 1) System.out.println(directed.toString()); - - Graph.Vertex start = v1; - if (debug > 1) System.out.println("Bellman-Ford's shortest paths of the directed graph from " + start.getValue()); - Map, Graph.CostPathPair> map2 = BellmanFord.getShortestPaths(directed, start); - if (debug > 1) System.out.println(getPathMapString(start, map2)); - - Graph.Vertex end = v3; - if (debug > 1) System.out.println("Bellman-Ford's shortest path of the directed graph from " + start.getValue() + " to " + end.getValue()); - Graph.CostPathPair pair2 = BellmanFord.getShortestPath(directed, start, end); - if (debug > 1) { - if (pair2 != null) System.out.println(pair2.toString()); - else System.out.println("No path from " + start.getValue() + " to " + end.getValue()); - } - - if (debug > 1) System.out.println("Johnson's all-pairs shortest path of the directed graph."); - Map, Map, Set>>> paths = Johnson.getAllPairsShortestPaths(directed); - if (debug > 1) { - if (paths == null) System.out.println("Directed graph contains a negative weight cycle."); - else System.out.println(getPathMapString(paths)); - } - - if (debug > 1) System.out.println("Floyd-Warshall's all-pairs shortest path weights of the directed graph."); - Map, Map, Integer>> pathWeights = FloydWarshall.getAllPairsShortestPaths(directed); - if (debug > 1) System.out.println(getWeightMapString(pathWeights)); - - if (debug > 1) System.out.println(); - } - - { // UNDIRECTED GRAPH - if (debug > 1) System.out.println("Undirected Graph cycle check."); - java.util.List> cycledVerticies = new ArrayList>(); - Graph.Vertex cv1 = new Graph.Vertex(1); - cycledVerticies.add(cv1); - Graph.Vertex cv2 = new Graph.Vertex(2); - cycledVerticies.add(cv2); - Graph.Vertex cv3 = new Graph.Vertex(3); - cycledVerticies.add(cv3); - Graph.Vertex cv4 = new Graph.Vertex(4); - cycledVerticies.add(cv4); - Graph.Vertex cv5 = new Graph.Vertex(5); - cycledVerticies.add(cv5); - Graph.Vertex cv6 = new Graph.Vertex(6); - cycledVerticies.add(cv6); - - java.util.List> cycledEdges = new ArrayList>(); - Graph.Edge ce1_2 = new Graph.Edge(7, cv1, cv2); - cycledEdges.add(ce1_2); - Graph.Edge ce2_4 = new Graph.Edge(15, cv2, cv4); - cycledEdges.add(ce2_4); - Graph.Edge ce3_4 = new Graph.Edge(11, cv3, cv4); - cycledEdges.add(ce3_4); - Graph.Edge ce3_6 = new Graph.Edge(2, cv3, cv6); - cycledEdges.add(ce3_6); - Graph.Edge ce5_6 = new Graph.Edge(9, cv5, cv6); - cycledEdges.add(ce5_6); - Graph.Edge ce4_5 = new Graph.Edge(6, cv4, cv5); - cycledEdges.add(ce4_5); - - Graph undirectedWithCycle = new Graph(cycledVerticies, cycledEdges); - if (debug > 1) System.out.println(undirectedWithCycle.toString()); - - if (debug > 1) { - System.out.println("Cycle detection of the undirected graph."); - boolean result = CycleDetection.detect(undirectedWithCycle); - System.out.println("result=" + result); - System.out.println(); - } - - java.util.List> verticies = new ArrayList>(); - Graph.Vertex v1 = new Graph.Vertex(1); - verticies.add(v1); - Graph.Vertex v2 = new Graph.Vertex(2); - verticies.add(v2); - Graph.Vertex v3 = new Graph.Vertex(3); - verticies.add(v3); - Graph.Vertex v4 = new Graph.Vertex(4); - verticies.add(v4); - Graph.Vertex v5 = new Graph.Vertex(5); - verticies.add(v5); - Graph.Vertex v6 = new Graph.Vertex(6); - verticies.add(v6); - - java.util.List> edges = new ArrayList>(); - Graph.Edge e1_2 = new Graph.Edge(7, v1, v2); - edges.add(e1_2); - Graph.Edge e2_4 = new Graph.Edge(15, v2, v4); - edges.add(e2_4); - Graph.Edge e3_4 = new Graph.Edge(11, v3, v4); - edges.add(e3_4); - Graph.Edge e3_6 = new Graph.Edge(2, v3, v6); - edges.add(e3_6); - Graph.Edge e4_5 = new Graph.Edge(6, v4, v5); - edges.add(e4_5); - - Graph undirectedWithoutCycle = new Graph(verticies, edges); - if (debug > 1) System.out.println(undirectedWithoutCycle.toString()); - - if (debug > 1) { - System.out.println("Cycle detection of the undirected graph."); - boolean result = CycleDetection.detect(undirectedWithoutCycle); - System.out.println("result=" + result); - System.out.println(); - } - } - - { // DIRECTED GRAPH - if (debug > 1) System.out.println("Directed Graph topological sort."); - java.util.List> verticies = new ArrayList>(); - Graph.Vertex cv1 = new Graph.Vertex(1); - verticies.add(cv1); - Graph.Vertex cv2 = new Graph.Vertex(2); - verticies.add(cv2); - Graph.Vertex cv3 = new Graph.Vertex(3); - verticies.add(cv3); - Graph.Vertex cv4 = new Graph.Vertex(4); - verticies.add(cv4); - Graph.Vertex cv5 = new Graph.Vertex(5); - verticies.add(cv5); - Graph.Vertex cv6 = new Graph.Vertex(6); - verticies.add(cv6); - - java.util.List> edges = new ArrayList>(); - Graph.Edge ce1_2 = new Graph.Edge(1, cv1, cv2); - edges.add(ce1_2); - Graph.Edge ce2_4 = new Graph.Edge(2, cv2, cv4); - edges.add(ce2_4); - Graph.Edge ce4_3 = new Graph.Edge(3, cv4, cv3); - edges.add(ce4_3); - Graph.Edge ce3_6 = new Graph.Edge(4, cv3, cv6); - edges.add(ce3_6); - Graph.Edge ce5_6 = new Graph.Edge(5, cv5, cv6); - edges.add(ce5_6); - Graph.Edge ce4_5 = new Graph.Edge(6, cv4, cv5); - edges.add(ce4_5); - - Graph directed = new Graph(Graph.TYPE.DIRECTED, verticies, edges); - if (debug > 1) System.out.println(directed.toString()); - - if (debug > 1) System.out.println("Topological sort of the directed graph."); - java.util.List> results = TopologicalSort.sort(directed); - if (debug > 1) { - System.out.println("result=" + results); - System.out.println(); - } - } - - return true; - } - - private static boolean testMinHeap() { - String aNameMin = "Min-Heap [array]"; - BinaryHeap.BinaryHeapArray aHeapMin = new BinaryHeap.BinaryHeapArray(BinaryHeap.Type.MIN); - Collection aCollectionMin = aHeapMin.toCollection(); - if((validateStructure||validateContents) && !testHeap(aHeapMin,aNameMin,BinaryHeap.Type.MIN)) return false; - if(!testJavaCollection(aCollectionMin,Type.Integer,aNameMin)) return false; - - String tNameMin = "Min-Heap [tree]"; - BinaryHeap.BinaryHeapTree tHeapMin = new BinaryHeap.BinaryHeapTree(BinaryHeap.Type.MIN); - Collection tCollectionMin = tHeapMin.toCollection(); - if((validateStructure||validateContents) && !testHeap(tHeapMin,tNameMin,BinaryHeap.Type.MIN)) return false; - if(!testJavaCollection(tCollectionMin,Type.Integer,tNameMin)) return false; - - return true; - } - - private static boolean testMaxHeap() { - String aNameMax = "Max-Heap [array]"; - BinaryHeap.BinaryHeapArray aHeapMax = new BinaryHeap.BinaryHeapArray(BinaryHeap.Type.MAX); - Collection aCollectionMax = aHeapMax.toCollection(); - if((validateStructure||validateContents) && !testHeap(aHeapMax,aNameMax,BinaryHeap.Type.MAX)) return false; - if(!testJavaCollection(aCollectionMax,Type.Integer,aNameMax)) return false; - - String lNameMax = "Max-Heap [tree]"; - BinaryHeap.BinaryHeapTree tHeapMax = new BinaryHeap.BinaryHeapTree(BinaryHeap.Type.MAX); - Collection tCollectionMax = tHeapMax.toCollection(); - if((validateStructure||validateContents) && !testHeap(tHeapMax,lNameMax,BinaryHeap.Type.MAX)) return false; - if(!testJavaCollection(tCollectionMax,Type.Integer,lNameMax)) return false; - - return true; - } - - private static boolean testHashMap() { - String mapName = "HashMap"; - HashMap map = new HashMap(unsorted.length/2); - java.util.Map jMap = map.toMap(); - - if((validateStructure||validateContents) && !testMap(map,Type.Integer,mapName)) return false; - if(!testJavaMap(jMap,Type.Integer,mapName)) return false; - return true; - } - - private static boolean testHAMT() { - String mapName = "HAMT"; - HashArrayMappedTrie map = new HashArrayMappedTrie(); - java.util.Map jMap = map.toMap(); - - if((validateStructure||validateContents) && !testMap(map,Type.Integer,mapName)) return false; - if(!testJavaMap(jMap,Type.Integer,mapName)) return false; - return true; - } - - private static boolean testIntervalTree() { - { // Interval tree - if (debug > 1) System.out.println("Interval Tree."); - java.util.List> intervals = new ArrayList>(); - intervals.add((new IntervalTree.IntervalData(2, 6, "RED"))); - intervals.add((new IntervalTree.IntervalData(3, 5, "ORANGE"))); - intervals.add((new IntervalTree.IntervalData(4, 11, "GREEN"))); - intervals.add((new IntervalTree.IntervalData(5, 10, "DARK_GREEN"))); - intervals.add((new IntervalTree.IntervalData(8, 12, "BLUE"))); - intervals.add((new IntervalTree.IntervalData(9, 14, "PURPLE"))); - intervals.add((new IntervalTree.IntervalData(13, 15, "BLACK"))); - IntervalTree tree = new IntervalTree(intervals); - if (debug > 1) System.out.println(tree); - - IntervalTree.IntervalData query = tree.query(2); - if (debug > 1) System.out.println("2: " + query); - - query = tree.query(4); // Stabbing query - if (debug > 1) System.out.println("4: " + query); - - query = tree.query(9); // Stabbing query - if (debug > 1) System.out.println("9: " + query); - - query = tree.query(1, 16); // Range query - if (debug > 1) System.out.println("1->16: " + query); - - query = tree.query(7, 14); // Range query - if (debug > 1) System.out.println("7->14: " + query); - - query = tree.query(14, 15); // Range query - if (debug > 1) System.out.println("14->15: " + query); - - if (debug > 1) System.out.println(); - } - - { // Lifespan Interval tree - if (debug > 1) System.out.println("Lifespan Interval Tree."); - java.util.List> intervals = new ArrayList>(); - intervals.add((new IntervalTree.IntervalData(1888, 1971, "Stravinsky"))); - intervals.add((new IntervalTree.IntervalData(1874, 1951, "Schoenberg"))); - intervals.add((new IntervalTree.IntervalData(1843, 1907, "Grieg"))); - intervals.add((new IntervalTree.IntervalData(1779, 1828, "Schubert"))); - intervals.add((new IntervalTree.IntervalData(1756, 1791, "Mozart"))); - intervals.add((new IntervalTree.IntervalData(1585, 1672, "Schuetz"))); - IntervalTree tree = new IntervalTree(intervals); - if (debug > 1) System.out.println(tree); - - IntervalTree.IntervalData query = tree.query(1890); - if (debug > 1) System.out.println("1890: " + query); - - query = tree.query(1909); // Stabbing query - if (debug > 1) System.out.println("1909: " + query); - - query = tree.query(1792, 1903); // Range query - if (debug > 1) System.out.println("1792->1903: " + query); - - query = tree.query(1776, 1799); // Range query - if (debug > 1) System.out.println("1776->1799: " + query); - - if (debug > 1) System.out.println(); - } - - return true; - } - - private static boolean testJavaHashMap() { - String mapName = "Java's HashMap"; - java.util.Map map = new java.util.HashMap(unsorted.length/2); - - if(!testJavaMap(map,Type.Integer,mapName)) return false; - return true; - } - - private static boolean testJavaMinHeap() { - java.util.PriorityQueue minArrayHeap = new java.util.PriorityQueue(10, - new Comparator() { - - @Override - public int compare(Integer arg0, Integer arg1) { - if (arg0.compareTo(arg1) < 0) - return -1; - else if (arg1.compareTo(arg0) < 0) - return 1; - return 0; - } - }); - if(!testJavaCollection(minArrayHeap,Type.Integer,"Java's Min-Heap [array]")) return false; - - return true; - } - - private static boolean testJavaMaxHeap() { - java.util.PriorityQueue maxArrayHeap = new java.util.PriorityQueue(10, - new Comparator() { - - @Override - public int compare(Integer arg0, Integer arg1) { - if (arg0.compareTo(arg1) > 0) - return -1; - else if (arg1.compareTo(arg0) > 0) - return 1; - return 0; - } - }); - if(!testJavaCollection(maxArrayHeap,Type.Integer,"Java's Max-Heap [array]")) return false; - return true; - } - - private static boolean testJavaArrayList() { - if(!testJavaCollection(new java.util.ArrayList(),Type.Integer,"Java's List [array]")) return false; - return true; - } - - private static boolean testJavaLinkedList() { - if(!testJavaCollection(new java.util.LinkedList(),Type.Integer,"Java's List [linked]")) return false; - return true; - } - - private static boolean testJavaArrayQueue() { - String aName = "Java's Queue [array]"; - java.util.Deque aCollection = new java.util.ArrayDeque(); - - if(!testJavaCollection(aCollection,Type.Integer,aName)) return false; - return true; - } - - private static boolean testJavaLinkedQueue() { - String lName = "Java's Queue [linked]"; - java.util.Deque lCollection = new java.util.LinkedList(); - - if(!testJavaCollection(lCollection,Type.Integer,lName)) return false; - return true; - } - - private static boolean testJavaRedBlackIntegerTree() { - String aName = "Java's Red-Black Tree [Integer]"; - java.util.TreeSet aCollection = new java.util.TreeSet(); - if(!testJavaCollection(aCollection,Type.Integer,aName)) return false; - return true; - } - - private static boolean testJavaRedBlackStringTree() { - String aName = "Java's Red-Black Tree [String]"; - java.util.TreeSet aCollection = new java.util.TreeSet(); - if(!testJavaCollection(aCollection,Type.String,aName)) return false; - return true; - } - - private static boolean testJavaStack() { - String aName = "Java's Stack [array]"; - java.util.Stack aCollection = new java.util.Stack(); - if(!testJavaCollection(aCollection,Type.Integer,aName)) return false; - return true; - } - - private static boolean testJavaTreeMap() { - String mapName = "Java's TreeMap"; - java.util.Map map = new java.util.TreeMap(); - - if(!testJavaMap(map,Type.String,mapName)) return false; - return true; - } - - private static boolean testKdTree() { - if (debug > 1) System.out.println("k-d tree with node."); - - java.util.List points = new ArrayList(); - KdTree.XYZPoint p1 = new KdTree.XYZPoint(2, 3); - points.add(p1); - KdTree.XYZPoint p2 = new KdTree.XYZPoint(5, 4); - points.add(p2); - KdTree.XYZPoint p3 = new KdTree.XYZPoint(9, 6); - points.add(p3); - KdTree.XYZPoint p4 = new KdTree.XYZPoint(4, 7); - points.add(p4); - KdTree.XYZPoint p5 = new KdTree.XYZPoint(8, 1); - points.add(p5); - KdTree.XYZPoint p6 = new KdTree.XYZPoint(7, 2); - points.add(p6); - KdTree kdTree = new KdTree(points); - if (debug > 1) System.out.println(kdTree.toString()); - - Collection result = kdTree.nearestNeighbourSearch(1, p3); - if (debug > 1) System.out.println("NNS for " + p3 + " result=" + result + "\n"); - - KdTree.XYZPoint search = new KdTree.XYZPoint(1, 4); - result = kdTree.nearestNeighbourSearch(4, search); - if (debug > 1) System.out.println("NNS for " + search + " result=" + result + "\n"); - - kdTree.remove(p6); - if (debug > 1) System.out.println("Removed " + p6 + "\n" + kdTree.toString()); - kdTree.remove(p4); - if (debug > 1) System.out.println("Removed " + p4 + "\n" + kdTree.toString()); - kdTree.remove(p3); - if (debug > 1) System.out.println("Removed " + p3 + "\n" + kdTree.toString()); - kdTree.remove(p5); - if (debug > 1) System.out.println("Removed " + p5 + "\n" + kdTree.toString()); - kdTree.remove(p1); - if (debug > 1) System.out.println("Removed " + p1 + "\n" + kdTree.toString()); - kdTree.remove(p2); - if (debug > 1) System.out.println("Removed " + p2 + "\n" + kdTree.toString()); - - if (debug > 1) System.out.println(); - - return true; - } - - private static boolean testArrayList() { - String aName = "List [array]"; - List.ArrayList aList = new List.ArrayList(); - Collection aCollection = aList.toCollection(); - - if((validateStructure||validateContents) && !testList(aList,aName)) return false; - if(!testJavaCollection(aCollection,Type.Integer,aName)) return false; - return true; - } - - private static boolean testLinkedList() { - String lName = "List [linked]"; - List.LinkedList lList = new List.LinkedList(); - Collection lCollection = lList.toCollection(); - - if((validateStructure||validateContents) && !testList(lList,lName)) return false; - if(!testJavaCollection(lCollection,Type.Integer,lName)) return false; - return true; - } - - private static boolean testMatrix() { - if (debug > 1) System.out.println("Matrix."); - Matrix matrix1 = new Matrix(4, 3); - matrix1.set(0, 0, 14); - matrix1.set(0, 1, 9); - matrix1.set(0, 2, 3); - matrix1.set(1, 0, 2); - matrix1.set(1, 1, 11); - matrix1.set(1, 2, 15); - matrix1.set(2, 0, 0); - matrix1.set(2, 1, 12); - matrix1.set(2, 2, 17); - matrix1.set(3, 0, 5); - matrix1.set(3, 1, 2); - matrix1.set(3, 2, 3); - - Matrix matrix2 = new Matrix(3, 2); - matrix2.set(0, 0, 12); - matrix2.set(0, 1, 25); - matrix2.set(1, 0, 9); - matrix2.set(1, 1, 10); - matrix2.set(2, 0, 8); - matrix2.set(2, 1, 5); - - if (debug > 1) System.out.println("Matrix multiplication."); - Matrix matrix3 = matrix1.multiply(matrix2); - if (debug > 1) System.out.println(matrix3); - - int rows = 2; - int cols = 2; - int counter = 0; - Matrix matrix4 = new Matrix(rows, cols); - for (int r = 0; r < rows; r++) { - for (int c = 0; c < cols; c++) { - matrix4.set(r, c, counter++); - } - } - - if (debug > 1) System.out.println("Matrix subtraction."); - Matrix matrix5 = matrix4.subtract(matrix4); - if (debug > 1) System.out.println(matrix5); - - if (debug > 1) System.out.println("Matrix addition."); - Matrix matrix6 = matrix4.add(matrix4); - if (debug > 1) System.out.println(matrix6); - - Matrix matrix7 = new Matrix(2, 2); - matrix7.set(0, 0, 1); - matrix7.set(0, 1, 2); - matrix7.set(1, 0, 3); - matrix7.set(1, 1, 4); - - Matrix matrix8 = new Matrix(2, 2); - matrix8.set(0, 0, 1); - matrix8.set(0, 1, 2); - matrix8.set(1, 0, 3); - matrix8.set(1, 1, 4); - - if (debug > 1) System.out.println("Matrix multiplication."); - Matrix matrix9 = matrix7.multiply(matrix8); - if (debug > 1) System.out.println(matrix9); - - return true; - } - - private static boolean testPatriciaTrie() { - String bstName = "PatriciaTrie"; - PatriciaTrie bst = new PatriciaTrie(); - Collection bstCollection = bst.toCollection(); - - if((validateStructure||validateContents) && !testTree(bst,Type.String,bstName)) return false; - if(!testJavaCollection(bstCollection,Type.String,bstName)) return false; - return true; - } - - private static boolean testQuadTree() { - int size = 16000; - - java.util.Set set = new java.util.HashSet(size); - while (set.size() < size) { - float x = RANDOM.nextInt(size); - float y = RANDOM.nextInt(size); - QuadTree.XYPoint xyPoint = new QuadTree.XYPoint(x,y); - set.add(xyPoint); - } - - java.util.List query = new java.util.ArrayList(size); - for (int j=0; j tree = new QuadTree.PointRegionQuadTree(0,0,size,size); - - beforeMemory = DataStructures.getMemoryUse(); - { - beforeInsert = System.nanoTime(); - for (QuadTree.XYPoint p : set) { - tree.insert(p.getX(), p.getY()); - } - afterInsert = System.nanoTime(); - insertTime = afterInsert - beforeInsert; - System.out.println("PointRegionQuadTree insertTime="+insertTime/100000d+" ms"); - } - afterMemory = DataStructures.getMemoryUse(); - treeMemory = afterMemory - beforeMemory; - System.out.println("PointRegionQuadTree treeMemory="+treeMemory); - - //System.out.println(tree); - - // We should find all points here (tests structure) - for (QuadTree.XYPoint p : set) { - java.util.List result = tree.queryRange(p.getX(), p.getY(), 1, 1); - if (result.size()<=0) return false; - } - - // We should find all points here (tests query speed) - { - beforeQuery = System.nanoTime(); - for (QuadTree.XYPoint p : query) { - tree.queryRange(p.getX(), p.getY(), 1, 1); - } - afterQuery = System.nanoTime(); - queryTime = afterQuery - beforeQuery; - System.out.println("PointRegionQuadTree queryTime="+queryTime/100000d+" ms"); - } - - // Result set should not contain duplicates - beforeTreeQuery = System.nanoTime(); - java.util.List result = tree.queryRange(0, 0, size, size); - afterTreeQuery = System.nanoTime(); - treeQuery = afterTreeQuery - beforeTreeQuery; - System.out.println("PointRegionQuadTree wholeTreeQuery="+treeQuery/100000d+" ms"); - Collections.sort(result); - QuadTree.XYPoint prev = null; - for (QuadTree.XYPoint p : result) { - if (prev!=null && prev.equals(p)) return false; - prev = p; - } - - { // Remove all - beforeRemove = System.nanoTime(); - for (QuadTree.XYPoint p : set) { - //System.out.println(p.toString()); - boolean removed = tree.remove(p.getX(), p.getY()); - //System.out.println(tree.toString()); - if (!removed) return false; - } - afterRemove = System.nanoTime(); - removeTime = afterRemove - beforeRemove; - System.out.println("PointRegionQuadTree removeTime="+removeTime/100000d+" ms"); - } - } - - { // Rectangle base quadtree - QuadTree.MxCifQuadTree tree = new QuadTree.MxCifQuadTree(0,0,size,size,10,10); - beforeMemory = DataStructures.getMemoryUse(); - { - beforeInsert = System.nanoTime(); - for (QuadTree.XYPoint p : set) { - tree.insert(p.getX(), p.getY(), 1, 1); - } - afterInsert = System.nanoTime(); - insertTime = afterInsert - beforeInsert; - System.out.println("MxCifQuadTree insertTime="+insertTime/100000d+" ms"); - } - afterMemory = DataStructures.getMemoryUse(); - treeMemory = afterMemory - beforeMemory; - System.out.println("MxCifQuadTree treeMemory="+treeMemory); - - //System.out.println(tree); - - // We should find all points here - for (QuadTree.XYPoint p : set) { - java.util.List result = tree.queryRange(p.getX(), p.getY(), 1, 1); - if (result.size()<=0) return false; - } - - { // We should find all points here - beforeQuery = System.nanoTime(); - for (QuadTree.XYPoint p : query) { - tree.queryRange(p.getX(), p.getY(), 1, 1); - } - afterQuery = System.nanoTime(); - queryTime = afterQuery - beforeQuery; - System.out.println("MxCifQuadTree queryTime="+queryTime/100000d+" ms"); - } - - // Result set should not contain duplicates - beforeTreeQuery = System.nanoTime(); - java.util.List result = tree.queryRange(0, 0, size, size); - afterTreeQuery = System.nanoTime(); - treeQuery = afterTreeQuery - beforeTreeQuery; - System.out.println("MxCifQuadTree wholeTreeQuery="+treeQuery/100000d+" ms"); - Collections.sort(result); - QuadTree.AxisAlignedBoundingBox prev = null; - for (QuadTree.AxisAlignedBoundingBox p : result) { - if (prev!=null && prev.equals(p)) { - return false; - } - prev = p; - } - - { // Remove all - beforeRemove = System.nanoTime(); - for (QuadTree.XYPoint p : set) { - //System.out.println(p.toString()); - boolean removed = tree.remove(p.getX(), p.getY(), 1, 1); - //System.out.println(tree.toString()); - if (!removed) return false; - } - afterRemove = System.nanoTime(); - removeTime = afterRemove - beforeRemove; - System.out.println("MxCifQuadTree removeTime="+removeTime/100000d+" ms"); - } - } - - return true; - } - - private static boolean testArrayQueue() { - String aName = "Queue [array]"; - Queue.ArrayQueue aQueue = new Queue.ArrayQueue(); - Collection aCollection = aQueue.toCollection(); - - if((validateStructure||validateContents) && !testQueue(aQueue,aName)) return false; - if(!testJavaCollection(aCollection,Type.Integer,aName)) return false; - return true; - } - - private static boolean testLinkedQueue() { - String lName = "Queue [linked]"; - Queue.LinkedQueue lQueue = new Queue.LinkedQueue(); - Collection lCollection = lQueue.toCollection(); - - if((validateStructure||validateContents) && !testQueue(lQueue,lName)) return false; - if(!testJavaCollection(lCollection,Type.Integer,lName)) return false; - return true; - } - - private static boolean testRadixTrie() { - String mapName = "RadixTrie"; - RadixTrie map = new RadixTrie(); - java.util.Map jMap = map.toMap(); - - if((validateStructure||validateContents) && !testMap(map,Type.String,mapName)) return false; - if(!testJavaMap(jMap,Type.String,mapName)) return false; - return true; - } - - private static boolean testRedBlackTree() { - String bstName = "Red-Black Tree"; - RedBlackTree bst = new RedBlackTree(); - Collection bstCollection = bst.toCollection(); - - if((validateStructure||validateContents) && !testTree(bst,Type.Integer,bstName)) return false; - if(!testJavaCollection(bstCollection,Type.Integer,bstName)) return false; - - return true; - } - - private static boolean testSegmentTree() { - { // Quadrant Segment tree - if (debug > 1) System.out.println("Quadrant Segment Tree."); - java.util.List segments = new ArrayList(); - segments.add(new SegmentTree.Data.QuadrantData(0, 1, 0, 0, 0)); // first point in the 0th quadrant - segments.add(new SegmentTree.Data.QuadrantData(1, 0, 1, 0, 0)); // second point in the 1st quadrant - segments.add(new SegmentTree.Data.QuadrantData(2, 0, 0, 1, 0)); // third point in the 2nd quadrant - segments.add(new SegmentTree.Data.QuadrantData(3, 0, 0, 0, 1)); // fourth point in the 3rd quadrant - FlatSegmentTree tree = new FlatSegmentTree(segments); - if (debug > 1) System.out.println(tree); - - SegmentTree.Data.QuadrantData query = tree.query(0, 3); - if (debug > 1) System.out.println("0->3: " + query + "\n"); - - query = tree.query(2, 3); - if (debug > 1) System.out.println("2->3: " + query + "\n"); - - query = tree.query(0, 2); - if (debug > 1) System.out.println("0->2: " + query + "\n"); - - if (debug > 1) System.out.println(); - } - - { // Range Maximum Segment tree - if (debug > 1) System.out.println("Range Maximum Segment Tree."); - java.util.List> segments = new ArrayList>(); - segments.add(new SegmentTree.Data.RangeMaximumData(0, (Integer) 4)); - segments.add(new SegmentTree.Data.RangeMaximumData(1, (Integer) 2)); - segments.add(new SegmentTree.Data.RangeMaximumData(2, (Integer) 6)); - segments.add(new SegmentTree.Data.RangeMaximumData(3, (Integer) 3)); - segments.add(new SegmentTree.Data.RangeMaximumData(4, (Integer) 1)); - segments.add(new SegmentTree.Data.RangeMaximumData(5, (Integer) 5)); - segments.add(new SegmentTree.Data.RangeMaximumData(6, (Integer) 0)); - segments.add(new SegmentTree.Data.RangeMaximumData(7, 17, 7)); - segments.add(new SegmentTree.Data.RangeMaximumData(21, (Integer) 10)); - FlatSegmentTree> tree = new FlatSegmentTree>(segments, 3); - if (debug > 1) System.out.println(tree); - - SegmentTree.Data.RangeMaximumData query = tree.query(0, 7); - if (debug > 1) System.out.println("0->7: " + query + "\n"); - - query = tree.query(0, 21); - if (debug > 1) System.out.println("0->21: " + query + "\n"); - - query = tree.query(2, 5); - if (debug > 1) System.out.println("2->5: " + query + "\n"); - - query = tree.query(7); - if (debug > 1) System.out.println("7: " + query + "\n"); - - if (debug > 1) System.out.println(); - } - - { // Range Minimum Segment tree - if (debug > 1) System.out.println("Range Minimum Segment Tree."); - java.util.List> segments = new ArrayList>(); - segments.add(new SegmentTree.Data.RangeMinimumData(0, (Integer) 4)); - segments.add(new SegmentTree.Data.RangeMinimumData(1, (Integer) 2)); - segments.add(new SegmentTree.Data.RangeMinimumData(2, (Integer) 6)); - segments.add(new SegmentTree.Data.RangeMinimumData(3, (Integer) 3)); - segments.add(new SegmentTree.Data.RangeMinimumData(4, (Integer) 1)); - segments.add(new SegmentTree.Data.RangeMinimumData(5, (Integer) 5)); - segments.add(new SegmentTree.Data.RangeMinimumData(6, (Integer) 0)); - segments.add(new SegmentTree.Data.RangeMinimumData(17, (Integer) 7)); - FlatSegmentTree> tree = new FlatSegmentTree>(segments, 5); - if (debug > 1) System.out.println(tree); - - SegmentTree.Data.RangeMinimumData query = tree.query(0, 7); - if (debug > 1) System.out.println("0->7: " + query + "\n"); - - query = tree.query(0, 17); - if (debug > 1) System.out.println("0->17: " + query + "\n"); - - query = tree.query(1, 3); - if (debug > 1) System.out.println("1->3: " + query + "\n"); - - query = tree.query(7); - if (debug > 1) System.out.println("7: " + query + "\n"); - - if (debug > 1) System.out.println(); - } - - { // Range Sum Segment tree - if (debug > 1) System.out.println("Range Sum Segment Tree."); - java.util.List> segments = new ArrayList>(); - segments.add(new SegmentTree.Data.RangeSumData(0, (Integer) 4)); - segments.add(new SegmentTree.Data.RangeSumData(1, (Integer) 2)); - segments.add(new SegmentTree.Data.RangeSumData(2, (Integer) 6)); - segments.add(new SegmentTree.Data.RangeSumData(3, (Integer) 3)); - segments.add(new SegmentTree.Data.RangeSumData(4, (Integer) 1)); - segments.add(new SegmentTree.Data.RangeSumData(5, (Integer) 5)); - segments.add(new SegmentTree.Data.RangeSumData(6, (Integer) 0)); - segments.add(new SegmentTree.Data.RangeSumData(17, (Integer) 7)); - FlatSegmentTree> tree = new FlatSegmentTree>(segments, 10); - if (debug > 1) System.out.println(tree); - - SegmentTree.Data.RangeSumData query = tree.query(0, 8); - if (debug > 1) System.out.println("0->8: " + query + "\n"); - - query = tree.query(0, 17); - if (debug > 1) System.out.println("0->17: " + query + "\n"); - - query = tree.query(2, 5); - if (debug > 1) System.out.println("2->5: " + query + "\n"); - - query = tree.query(10, 17); - if (debug > 1) System.out.println("10->17: " + query + "\n"); - - query = tree.query(16); - if (debug > 1) System.out.println("16: " + query + "\n"); - - query = tree.query(17); - if (debug > 1) System.out.println("17: " + query + "\n"); - - if (debug > 1) System.out.println(); - } - - { // Interval Segment tree - if (debug > 1) System.out.println("Interval Segment Tree."); - java.util.List> segments = new ArrayList>(); - segments.add((new SegmentTree.Data.IntervalData(2, 6, "RED"))); - segments.add((new SegmentTree.Data.IntervalData(3, 5, "ORANGE"))); - segments.add((new SegmentTree.Data.IntervalData(4, 11, "GREEN"))); - segments.add((new SegmentTree.Data.IntervalData(5, 10, "DARK_GREEN"))); - segments.add((new SegmentTree.Data.IntervalData(8, 12, "BLUE"))); - segments.add((new SegmentTree.Data.IntervalData(9, 14, "PURPLE"))); - segments.add((new SegmentTree.Data.IntervalData(13, 15, "BLACK"))); - DynamicSegmentTree> tree = new DynamicSegmentTree>(segments); - if (debug > 1) System.out.println(tree); - - SegmentTree.Data.IntervalData query = tree.query(2); - if (debug > 1) System.out.println("2: " + query); - - query = tree.query(4); // Stabbing query - if (debug > 1) System.out.println("4: " + query); - - query = tree.query(9); // Stabbing query - if (debug > 1) System.out.println("9: " + query); - - query = tree.query(1, 16); // Range query - if (debug > 1) System.out.println("1->16: " + query); - - query = tree.query(7, 14); // Range query - if (debug > 1) System.out.println("7->14: " + query); - - query = tree.query(14, 15); // Range query - if (debug > 1) System.out.println("14->15: " + query); - - if (debug > 1) System.out.println(); - } - - { // Lifespan Interval Segment tree - if (debug > 1) System.out.println("Lifespan Interval Segment Tree."); - java.util.List> segments = new ArrayList>(); - segments.add((new SegmentTree.Data.IntervalData(1888, 1971, "Stravinsky"))); - segments.add((new SegmentTree.Data.IntervalData(1874, 1951, "Schoenberg"))); - segments.add((new SegmentTree.Data.IntervalData(1843, 1907, "Grieg"))); - segments.add((new SegmentTree.Data.IntervalData(1779, 1828, "Schubert"))); - segments.add((new SegmentTree.Data.IntervalData(1756, 1791, "Mozart"))); - segments.add((new SegmentTree.Data.IntervalData(1585, 1672, "Schuetz"))); - DynamicSegmentTree> tree = new DynamicSegmentTree>(segments, 25); - if (debug > 1) System.out.println(tree); - - SegmentTree.Data.IntervalData query = tree.query(1890); - if (debug > 1) System.out.println("1890: " + query); - - query = tree.query(1909); // Stabbing query - if (debug > 1) System.out.println("1909: " + query); - - query = tree.query(1792, 1903); // Range query - if (debug > 1) System.out.println("1792->1903: " + query); - - query = tree.query(1776, 1799); // Range query - if (debug > 1) System.out.println("1776->1799: " + query); - - if (debug > 1) System.out.println(); - } - - return true; - } - - private static boolean testJavaSkipList() { - String sName = "Java's SkipList"; - NavigableSet sList = new ConcurrentSkipListSet(); - Collection lCollection = sList; - if(!testJavaCollection(lCollection,Type.Integer,sName)) return false; - return true; - } - - private static boolean testSkipList() { - String sName = "SkipList"; - SkipList sList = new SkipList(); - Collection lCollection = sList.toCollection(); - - if((validateStructure||validateContents) && !testSet(sList,sName)) return false; - if(!testJavaCollection(lCollection,Type.Integer,sName)) return false; - return true; - } - - private static boolean testSplayTree() { - String bstName = "Splay Tree"; - BinarySearchTree bst = new SplayTree(); - Collection bstCollection = bst.toCollection(); - - if((validateStructure||validateContents) && !testTree(bst,Type.Integer,bstName)) return false; - if(!testJavaCollection(bstCollection,Type.Integer,bstName)) return false; - - return true; - } - - private static boolean testArrayStack() { - String aName = "Stack [array]"; - Stack.ArrayStack aStack = new Stack.ArrayStack(); - Collection aCollection = aStack.toCollection(); - - if((validateStructure||validateContents) && !testStack(aStack,aName)) return false; - if(!testJavaCollection(aCollection,Type.Integer,aName)) return false; - return true; - } - - private static boolean testLinkedStack() { - String lName = "Stack [linked]"; - Stack.LinkedStack lStack = new Stack.LinkedStack(); - Collection lCollection = lStack.toCollection(); - - if((validateStructure||validateContents) && !testStack(lStack,lName)) return false; - if(!testJavaCollection(lCollection,Type.Integer,lName)) return false; - return true; - } - - private static boolean testSuffixTree() { - if (debug > 1) System.out.println("Suffix Tree."); - String bookkeeper = "bookkeeper"; - SuffixTree tree = new SuffixTree(bookkeeper); - if (debug > 1) System.out.println(tree.toString()); - if (debug > 1) System.out.println(tree.getSuffixes()); - - boolean exists = tree.doesSubStringExist(bookkeeper); - if (!exists) { - System.err.println("YIKES!! " + bookkeeper + " doesn't exists."); - handleError(tree); - return false; - } - - String failed = "booker"; - exists = tree.doesSubStringExist(failed); - if (exists) { - System.err.println("YIKES!! " + failed + " exists."); - handleError(tree); - return false; - } - - String pass = "kkee"; - exists = tree.doesSubStringExist(pass); - if (!exists) { - System.err.println("YIKES!! " + pass + " doesn't exists."); - handleError(tree); - return false; - } - - if (debug > 1) System.out.println(); - - return true; - } - - private static boolean testSuffixTrie() { - if (debug > 1) System.out.println("Suffix Trie."); - String bookkeeper = "bookkeeper"; - SuffixTrie trie = new SuffixTrie(bookkeeper); - if (debug > 1) System.out.println(trie.toString()); - if (debug > 1) System.out.println(trie.getSuffixes()); - - boolean exists = trie.doesSubStringExist(bookkeeper); - if (!exists) { - System.err.println("YIKES!! " + bookkeeper + " doesn't exists."); - handleError(trie); - return false; - } - - String failed = "booker"; - exists = trie.doesSubStringExist(failed); - if (exists) { - System.err.println("YIKES!! " + failed + " exists."); - handleError(trie); - return false; - } - - String pass = "kkee"; - exists = trie.doesSubStringExist(pass); - if (!exists) { - System.err.println("YIKES!! " + pass + " doesn't exists."); - handleError(trie); - return false; - } - - if (debug > 1) System.out.println(); - - return true; - } - - private static boolean testTreap() { - String bstName = "Treap"; - BinarySearchTree bst = new Treap(); - Collection bstCollection = bst.toCollection(); - - if((validateStructure||validateContents) && !testTree(bst,Type.Integer,bstName)) return false; - if(!testJavaCollection(bstCollection,Type.Integer,bstName)) return false; - return true; - } - - private static boolean testTreeMap() { - String mapName = "TreeMap"; - TreeMap map = new TreeMap(); - java.util.Map jMap = map.toMap(); - - if((validateStructure||validateContents) && !testMap(map,Type.String,mapName)) return false; - if(!testJavaMap(jMap,Type.Integer,mapName)) return false; - return true; - } - - private static boolean testTrie() { - String bstName = "Trie"; - Trie bst = new Trie(); - Collection bstCollection = bst.toCollection(); - - if((validateStructure||validateContents) && !testTree(bst,Type.String,bstName)) return false; - if(!testJavaCollection(bstCollection,Type.String,bstName)) return false; - return true; - } - - private static boolean testTrieMap() { - String mapName = "TrieMap"; - TrieMap map = new TrieMap(); - java.util.Map jMap = map.toMap(); - - if((validateStructure||validateContents) && !testMap(map,Type.String,mapName)) return false; - if(!testJavaMap(jMap,Type.String,mapName)) return false; - return true; - } - - private static boolean testJavaSkipListMap() { - String mapName = "Java's SkipListMap"; - ConcurrentSkipListMap map = new ConcurrentSkipListMap(); - if(!testJavaMap(map,Type.Integer,mapName)) return false; - return true; - } - - private static boolean testSkipListMap() { - String mapName = "SkipListMap"; - SkipListMap map = new SkipListMap(); - java.util.Map jMap = map.toMap(); - - if((validateStructure||validateContents) && !testMap(map,Type.String,mapName)) return false; - if(!testJavaMap(jMap,Type.Integer,mapName)) return false; - return true; - } - - private static boolean testMap(IMap map, Type keyType, String name) { - for (int i = 0; i < unsorted.length; i++) { - Integer item = unsorted[i]; - K k = null; - V v = null; - if (keyType == Type.Integer) { - k = (K)item; - v = (V)String.valueOf(item); - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - v = (V)item; - } - V added = map.put(k,v); - if (validateStructure && (!map.validate() || (map.size() != (i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(map); - return false; - } - if (validateContents && (added!=null || !map.contains(k))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists."); - handleError(map); - return false; - } - } - - K invalidKey = null; - if (keyType == Type.Integer) { - invalidKey = (K)INVALID; - } else if (keyType == Type.String) { - invalidKey = (K)String.valueOf(INVALID); - } - boolean contains = map.contains(invalidKey); - V removed = map.remove(invalidKey); - if (contains || (removed!=null)) { - System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); - handleError(map); - return false; - } - - for (int i = 0; i < unsorted.length; i++) { - Integer item = unsorted[i]; - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - removed = map.remove(k); - if (validateStructure && (!map.validate() || (map.size() != (unsorted.length-(i+1))))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(map); - return false; - } - if (validateContents && map.contains(k)) { - System.err.println(name+" YIKES!! " + item + " still exists."); - handleError(map); - return false; - } - } - - // Add half, remove a quarter, add three-quarters, remove all - int quarter = unsorted.length/4; - int half = unsorted.length/2; - for (int i = 0; i < half; i++) { - Integer item = unsorted[i]; - K k = null; - V v = null; - if (keyType == Type.Integer) { - k = (K)item; - v = (V)String.valueOf(item); - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - v = (V)item; - } - V added = map.put(k,v); - if (validateStructure && (!map.validate() || (map.size() != (i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(map); - return false; - } - if (validateContents && (added!=null || !map.contains(k))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists."); - handleError(map); - return false; - } - } - for (int i = (half-1); i >= quarter; i--) { - Integer item = unsorted[i]; - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - removed = map.remove(k); - if (validateStructure && (!map.validate() || (map.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(map); - return false; - } - if (validateContents && (removed==null || map.contains(k))) { - System.err.println(name+" YIKES!! " + item + " still exists."); - handleError(map); - return false; - } - } - for (int i = quarter; i < unsorted.length; i++) { - Integer item = unsorted[i]; - K k = null; - V v = null; - if (keyType == Type.Integer) { - k = (K)item; - v = (V)String.valueOf(item); - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - v = (V)item; - } - V added = map.put(k,v); - if (validateStructure && (!map.validate() || (map.size() != (i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(map); - return false; - } - if (validateContents && (added!=null || !map.contains(k))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists."); - handleError(map); - return false; - } - } - for (int i = unsorted.length-1; i >= 0; i--) { - Integer item = unsorted[i]; - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - removed = map.remove(k); - if (validateStructure && (!map.validate() || (map.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(map); - return false; - } - if (validateContents && (removed==null || map.contains(k))) { - System.err.println(name+" YIKES!! " + item + " still exists."); - handleError(map); - return false; - } - } - - if (validateStructure && (map.size() != 0)) { - System.err.println(name+" YIKES!! a size mismatch."); - handleError(map); - return false; - } - - return true; - } - - /** - * Tests the tree operations - * - * @param tree to test. - * @return True is works as a tree structure. - */ - private static > boolean testTree(ITree tree, Type type, String name) { - for (int i = 0; i < unsorted.length; i++) { - Integer value = unsorted[i]; - T item = null; - if (type == Type.Integer) { - item = (T)value; - } else if (type == Type.String) { - item = (T)String.valueOf(value); - } - boolean added = tree.add(item); - if (validateStructure && (!tree.validate() || (tree.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(tree); - return false; - } - if (validateContents && (!added || !tree.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(tree); - return false; - } - } - - T invalidItem = null; - if (type == Type.Integer) { - invalidItem = (T)INVALID; - } else if (type == Type.String) { - invalidItem = (T)String.valueOf(INVALID); - } - boolean contains = tree.contains(invalidItem); - T removed = tree.remove(invalidItem); - if (contains || removed!=null) { - System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); - handleError(tree); - return false; - } - - int size = tree.size(); - for (int i = 0; i < size; i++) { - Integer value = unsorted[i]; - T item = null; - if (type == Type.Integer) { - item = (T)value; - } else if (type == Type.String) { - item = (T)String.valueOf(value); - } - removed = tree.remove(item); - if (validateStructure && (!tree.validate() || (tree.size() != unsorted.length-(i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(tree); - return false; - } - if (validateContents && (removed==null || tree.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been removed."); - handleError(tree); - return false; - } - } - - // Add half, remove a quarter, add three-quarters - int quarter = unsorted.length/4; - int half = unsorted.length/2; - for (int i = 0; i < half; i++) { - Integer value = unsorted[i]; - T item = null; - if (type == Type.Integer) { - item = (T)value; - } else if (type == Type.String) { - item = (T)String.valueOf(value); - } - boolean added = tree.add(item); - if (validateStructure && (!tree.validate() || (tree.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(tree); - return false; - } - if (validateContents && (!added || !tree.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(tree); - return false; - } - } - for (int i = (half-1); i >= quarter; i--) { - Integer value = unsorted[i]; - T item = null; - if (type == Type.Integer) { - item = (T)value; - } else if (type == Type.String) { - item = (T)String.valueOf(value); - } - removed = tree.remove(item); - if (validateStructure && (!tree.validate() || (tree.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(tree); - return false; - } - if (validateContents && (removed==null || tree.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(tree); - return false; - } - } - for (int i = quarter; i < unsorted.length; i++) { - Integer value = unsorted[i]; - T item = null; - if (type == Type.Integer) { - item = (T)value; - } else if (type == Type.String) { - item = (T)String.valueOf(value); - } - boolean added = tree.add(item); - if (validateStructure && (!tree.validate() || (tree.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(tree); - return false; - } - if (validateContents && (!added || !tree.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(tree); - return false; - } - } - for (int i = unsorted.length-1; i >= 0; i--) { - Integer value = unsorted[i]; - T item = null; - if (type == Type.Integer) { - item = (T)value; - } else if (type == Type.String) { - item = (T)String.valueOf(value); - } - removed = tree.remove(item); - if (validateStructure && (!tree.validate() || (tree.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(tree); - return false; - } - if (validateContents && (removed==null || tree.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(tree); - return false; - } - } - - if (validateStructure && (tree.size() != 0)) { - System.err.println(name+" YIKES!! a size mismatch."); - handleError(tree); - return false; - } - - return true; - } - - /** - * Tests the actual heap like operations - * - * @param heap to test. - * @return True is works as a heap structure. - */ - private static > boolean testHeap(IHeap heap, String name, BinaryHeap.Type type) { - for (int i = 0; i < unsorted.length; i++) { - T item = (T)unsorted[i]; - boolean added = heap.add(item); - if (validateStructure && (!heap.validate() || (heap.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(heap); - return false; - } - if (validateContents && (!added || !heap.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(heap); - return false; - } - } - - boolean contains = heap.contains((T)INVALID); - T removed = heap.remove((T)INVALID); - if (contains || (removed!=null)) { - System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); - handleError(heap); - return false; - } - - int size = heap.size(); - for (int i = 0; i < size; i++) { - T item = heap.removeHead(); - T correct = (T)((type == BinaryHeap.Type.MIN)?sorted[i]:sorted[sorted.length-(i+1)]); - if (validateStructure && (item.compareTo(correct)!=0)) { - System.err.println(name+" YIKES!! " + item + " does not match heap item."); - handleError(heap); - return false; - } - if (validateStructure && (!heap.validate() || (heap.size() != unsorted.length-(i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(heap); - return false; - } - if (validateContents && (item==null || heap.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(heap); - return false; - } - } - - // Add half, remove a quarter, add three-quarters, remove all - int quarter = unsorted.length/4; - int half = unsorted.length/2; - Integer[] halfArray = Arrays.copyOf(unsorted, half); - Arrays.sort(halfArray); - Integer[] quarterArray = new Integer[quarter]; - Integer[] sortedQuarterArray = new Integer[quarter]; //Needed for binary search - for (int i=0; i=0) { - threeQuartersArray[idx++] = i; - } else { - index = Arrays.binarySearch(halfArray, i); - if (index<0) threeQuartersArray[idx++] = i; - } - } else { - if (index>=0) { - threeQuartersArray[idx++] = i; - } else { - index = Arrays.binarySearch(halfArray, i); - if (index<0) threeQuartersArray[idx++] = i; - } - } - } - for (int i = 0; i < half; i++) { - T item = (T)unsorted[i]; - boolean added = heap.add(item); - if (validateStructure && (!heap.validate() || (heap.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(heap); - return false; - } - if (validateContents && (!added || !heap.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(heap); - return false; - } - } - for (int i = 0; i < quarter; i++) { - T item = heap.removeHead(); - T correct = (T)quarterArray[i]; - if (validateStructure && (item.compareTo(correct)!=0)) { - System.err.println(name+" YIKES!! " + item + " does not match heap item."); - handleError(heap); - return false; - } - if (validateStructure && (!heap.validate() || (heap.size() != half-(i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(heap); - return false; - } - if (validateContents && (item==null || heap.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(heap); - return false; - } - } - for (int i = 0; i < threeQuartersArray.length; i++) { - T item = (T)threeQuartersArray[i]; - boolean added = heap.add(item); - if (validateStructure && (!heap.validate() || (heap.size() != quarter+(i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(heap); - return false; - } - if (validateContents && (!added || !heap.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(heap); - return false; - } - } - for (int i = 0; i < sorted.length; i++) { - T item = heap.removeHead(); - T correct = (T)((type == BinaryHeap.Type.MIN)?sorted[i]:sorted[sorted.length-(i+1)]); - if (validateStructure && (item.compareTo(correct)!=0)) { - System.err.println(name+" YIKES!! " + item + " does not match heap item."); - handleError(heap); - return false; - } - if (validateStructure && (!heap.validate() || (heap.size() != unsorted.length-(i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(heap); - return false; - } - if (validateContents && (item==null || heap.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(heap); - return false; - } - } - - if (validateStructure && (heap.size() != 0)) { - System.err.println(name+" YIKES!! a size mismatch."); - handleError(heap); - return false; - } - - return true; - } - - /** - * Tests the actual queue like (FIFO) operations - * - * @param queue to test. - * @return True is works as a FIFO structure. - */ - private static > boolean testQueue(IQueue queue, String name) { - for (int i = 0; i < unsorted.length; i++) { - T item = (T)unsorted[i]; - boolean added = queue.offer(item); - if (validateStructure && (!queue.validate() || (queue.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(queue); - return false; - } - if (validateContents && (!added || !queue.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(queue); - return false; - } - } - - boolean contains = queue.contains((T)INVALID); - boolean removed = queue.remove((T)INVALID); - if (contains || removed) { - System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); - handleError(queue); - return false; - } - - int size = queue.size(); - for (int i = 0; i < size; i++) { - T item = queue.poll(); - T correct = (T)unsorted[i]; - if (validateStructure && (item.compareTo(correct)!=0)) { - System.err.println(name+" YIKES!! " + item + " does not match FIFO item."); - handleError(queue); - return false; - } - if (validateStructure && (!queue.validate() || (queue.size() != unsorted.length-(i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(queue); - return false; - } - if (validateContents && (item==null || queue.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(queue); - return false; - } - } - - // Add half, remove a quarter, add three-quarters - int quarter = unsorted.length/4; - int half = unsorted.length/2; - int changeOver = half-quarter; - for (int i = 0; i < half; i++) { - T item = (T)unsorted[i]; - boolean added = queue.offer(item); - if (validateStructure && (!queue.validate() || (queue.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(queue); - return false; - } - if (validateContents && (!added || !queue.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(queue); - return false; - } - } - for (int i = 0; i < quarter; i++) { - T item = queue.poll(); - T correct = (T)unsorted[i]; - if (validateStructure && (item.compareTo(correct)!=0)) { - System.err.println(name+" YIKES!! " + item + " does not match FIFO item."); - handleError(queue); - return false; - } - if (validateStructure && (!queue.validate() || (queue.size() != (half-(i+1))))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(queue); - return false; - } - if (validateContents && (item==null || queue.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(queue); - return false; - } - } - for (int i = 0; i < quarter; i++) { - T item = (T)unsorted[i]; - boolean added = queue.offer(item); - if (validateStructure && (!queue.validate() || (queue.size() != ((half-quarter)+(i+1))))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(queue); - return false; - } - if (validateContents && (!added || !queue.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(queue); - return false; - } - } - for (int i = half; i < unsorted.length; i++) { - T item = (T)unsorted[i]; - boolean added = queue.offer(item); - if (validateStructure && (!queue.validate() || (queue.size() != (i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(queue); - return false; - } - if (validateContents && (!added || !queue.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(queue); - return false; - } - } - for (int i = 0; i < unsorted.length; i++) { - T item = queue.poll(); - int idx = i; - if (idx < changeOver) { - idx = quarter+i; - } else if (idx>=changeOver && idx> boolean testStack(IStack stack, String name) { - for (int i = 0; i < unsorted.length; i++) { - T item = (T)unsorted[i]; - boolean added = stack.push(item); - if (validateStructure && (!stack.validate() || (stack.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(stack); - return false; - } - if (validateContents && (!added || item==null || !stack.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(stack); - return false; - } - } - - boolean contains = stack.contains((T)INVALID); - boolean removed = stack.remove((T)INVALID); - if (contains || removed) { - System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); - handleError(stack); - return false; - } - - int size = stack.size(); - for (int i = 0; i < size; i++) { - T item = stack.pop(); - T correct = (T)unsorted[unsorted.length-(i+1)]; - if (validateStructure && (item.compareTo(correct)!=0)) { - System.err.println(name+" YIKES!! " + item + " does not match LIFO item."); - handleError(stack); - return false; - } - if (validateStructure && (!stack.validate() || (stack.size() != unsorted.length-(i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(stack); - return false; - } - if (validateContents && (item==null || stack.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(stack); - return false; - } - } - - // Add half, remove a quarter, add three-quarters, remove all - int quarter = unsorted.length/4; - int half = unsorted.length/2; - for (int i = 0; i < half; i++) { - T item = (T)unsorted[i]; - boolean added = stack.push(item); - if (validateStructure && (!stack.validate() || (stack.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(stack); - return false; - } - if (validateContents && (!added || item==null || !stack.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(stack); - return false; - } - } - for (int i = (half-1); i >= quarter; i--) { - T item = stack.pop(); - T correct = (T)unsorted[i]; - if (validateStructure && (item.compareTo(correct)!=0)) { - System.err.println(name+" YIKES!! " + item + " does not match LIFO item."); - handleError(stack); - return false; - } - if (validateStructure && (!stack.validate() || (stack.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(stack); - return false; - } - if (validateContents && (item==null || stack.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(stack); - return false; - } - } - for (int i = quarter; i < unsorted.length; i++) { - T item = (T)unsorted[i]; - boolean added = stack.push(item); - if (validateStructure && (!stack.validate() || (stack.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(stack); - return false; - } - if (validateContents && (!added || item==null || !stack.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(stack); - return false; - } - } - for (int i = unsorted.length-1; i >= 0; i--) { - T item = stack.pop(); - T correct = (T)unsorted[i]; - if (validateStructure && (item.compareTo(correct)!=0)) { - System.err.println(name+" YIKES!! " + item + " does not match LIFO item."); - handleError(stack); - return false; - } - if (validateStructure && (!stack.validate() || (stack.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(stack); - return false; - } - if (validateContents && (item==null || stack.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(stack); - return false; - } - } - - if (validateStructure && (stack.size() != 0)) { - System.err.println(name+" YIKES!! a size mismatch."); - handleError(stack); - return false; - } - - return true; - } - - /** - * Tests the actual list operations - * - * @param list to test. - * @return True is works as a list structure. - */ - private static > boolean testList(IList list, String name) { - for (int i = 0; i < unsorted.length; i++) { - T item = (T)unsorted[i]; - boolean added = list.add(item); - if (validateStructure && (!list.validate() || (list.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(list); - return false; - } - if (validateContents && (!added || !list.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(list); - return false; - } - } - - boolean contains = list.contains((T)INVALID); - boolean removed = list.remove((T)INVALID); - if (contains || removed) { - System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); - handleError(list); - return false; - } - - int size = list.size(); - for (int i = 0; i < size; i++) { - T item = (T)unsorted[i]; - removed = list.remove(item); - if (validateStructure && (!list.validate() || (list.size() != unsorted.length-(i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(list); - return false; - } - if (validateContents && (!removed || list.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(list); - return false; - } - } - - // Add half, remove a quarter, add three-quarters, remove all - int quarter = unsorted.length/4; - int half = unsorted.length/2; - for (int i = 0; i < half; i++) { - T item = (T)unsorted[i]; - boolean added = list.add(item); - if (validateStructure && (!list.validate() || (list.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(list); - return false; - } - if (validateContents && (!added || !list.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(list); - return false; - } - } - for (int i = (half-1); i >= quarter; i--) { - T item = (T)unsorted[i]; - removed = list.remove(item); - if (validateStructure && (!list.validate() || (list.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(list); - return false; - } - if (validateContents && (!removed || list.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(list); - return false; - } - } - for (int i = quarter; i < unsorted.length; i++) { - T item = (T)unsorted[i]; - boolean added = list.add(item); - if (validateStructure && (!list.validate() || (list.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(list); - return false; - } - if (validateContents && (!added || !list.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(list); - return false; - } - } - for (int i = unsorted.length-1; i >= 0; i--) { - T item = (T)unsorted[i]; - removed = list.remove(item); - if (validateStructure && (!list.validate() || (list.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(list); - return false; - } - if (validateContents && (!removed || list.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(list); - return false; - } - } - - if (validateStructure && (list.size() != 0)) { - System.err.println(name+" YIKES!! a size mismatch."); - handleError(list); - return false; - } - - return true; - } - - /** - * Tests the actual set operations - * - * @param set to test. - * @return True is works as a set structure. - */ - private static > boolean testSet(ISet set, String name) { - for (int i = 0; i < unsorted.length; i++) { - T item = (T)unsorted[i]; - boolean added = set.add(item); - if (validateStructure && (!set.validate() || (set.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(set); - return false; - } - if (validateContents && (!added || !set.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(set); - return false; - } - } - - boolean contains = set.contains((T)INVALID); - boolean removed = set.remove((T)INVALID); - if (contains || removed) { - System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); - handleError(set); - return false; - } - - int size = set.size(); - for (int i = 0; i < size; i++) { - T item = (T)unsorted[i]; - removed = set.remove(item); - if (validateStructure && (!set.validate() || (set.size() != unsorted.length-(i+1)))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(set); - return false; - } - if (validateContents && (!removed || set.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(set); - return false; - } - } - - // Add half, remove a quarter, add three-quarters, remove all - int quarter = unsorted.length/4; - int half = unsorted.length/2; - for (int i = 0; i < half; i++) { - T item = (T)unsorted[i]; - boolean added = set.add(item); - if (validateStructure && (!set.validate() || (set.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(set); - return false; - } - if (validateContents && (!added || !set.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(set); - return false; - } - } - for (int i = (half-1); i >= quarter; i--) { - T item = (T)unsorted[i]; - removed = set.remove(item); - if (validateStructure && (!set.validate() || (set.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(set); - return false; - } - if (validateContents && (!removed || set.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(set); - return false; - } - } - for (int i = quarter; i < unsorted.length; i++) { - T item = (T)unsorted[i]; - boolean added = set.add(item); - if (validateStructure && (!set.validate() || (set.size() != i+1))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(set); - return false; - } - if (validateContents && (!added || !set.contains(item))) { - System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); - handleError(set); - return false; - } - } - for (int i = unsorted.length-1; i >= 0; i--) { - T item = (T)unsorted[i]; - removed = set.remove(item); - if (validateStructure && (!set.validate() || (set.size() != i))) { - System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); - handleError(set); - return false; - } - if (validateContents && (!removed || set.contains(item))) { - System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); - handleError(set); - return false; - } - } - - if (validateStructure && (set.size() != 0)) { - System.err.println(name+" YIKES!! a size mismatch."); - handleError(set); - return false; - } - - return true; - } - - private static > boolean testJavaCollection(Collection collection, Type type, String name) { - // Make sure the collection is empty - if (!collection.isEmpty()) { - System.err.println(name+" initial isEmpty() failed."); - handleError(collection); - return false; - } - if (collection.size()!=0) { - System.err.println(name+" initial size() failed."); - handleError(collection); - return false; - } - - long sortedCount = 0; - long unsortedCount = 0; - - long addTime = 0L; - long removeTime = 0L; - - long beforeAddTime = 0L; - long afterAddTime = 0L; - long beforeRemoveTime = 0L; - long afterRemoveTime = 0L; - - long memory = 0L; - - long beforeMemory = 0L; - long afterMemory = 0L; - - long lookupTime = 0L; - - long beforeLookupTime = 0L; - long afterLookupTime = 0L; - - if (debug > 1) System.out.println(name); - testNames[testIndex] = name; - - unsortedCount++; - { // UNSORTED: Add and remove in order (from index zero to length) - beforeMemory = 0L; - afterMemory = 0L; - beforeAddTime = 0L; - afterAddTime = 0L; - if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); - if (debugTime) beforeAddTime = System.nanoTime(); - for (int i = 0; i < unsorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)unsorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(unsorted[i]); - } - boolean added = collection.add(item); - if (!added) { - System.err.println(name+" unsorted add failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterAddTime = System.nanoTime(); - addTime += afterAddTime - beforeAddTime; - if (debug > 0) System.out.println(name+" unsorted add time = " + (addTime / unsortedCount) + " ns"); - } - if (debugMemory) { - afterMemory = DataStructures.getMemoryUse(); - memory += afterMemory - beforeMemory; - if (debug > 0) System.out.println(name+" unsorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); - } - - if (debug > 1) System.out.println(collection.toString()); - - if (validateIterator && !testIterator(collection.iterator())) { - System.err.println(name+" unsorted iterator failed."); - handleError(collection); - return false; - } - - beforeLookupTime = 0L; - afterLookupTime = 0L; - if (debugTime) beforeLookupTime = System.nanoTime(); - for (int i = 0; i < unsorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)unsorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(unsorted[i]); - } - boolean contains = collection.contains(item); - if (!contains) { - System.err.println(name+" unsorted contains failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterLookupTime = System.nanoTime(); - lookupTime += afterLookupTime - beforeLookupTime; - if (debug > 0) System.out.println(name+" unsorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); - } - - beforeRemoveTime = 0L; - afterRemoveTime = 0L; - if (debugTime) beforeRemoveTime = System.nanoTime(); - for (int i = 0; i < unsorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)unsorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(unsorted[i]); - } - boolean removed = collection.remove(item); - if (!removed) { - System.err.println(name+" unsorted remove failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterRemoveTime = System.nanoTime(); - removeTime += afterRemoveTime - beforeRemoveTime; - if (debug > 0) System.out.println(name+" unsorted remove time = " + (removeTime / unsortedCount) + " ns"); - } - - if (!collection.isEmpty()) { - System.err.println(name+" unsorted isEmpty() failed."); - handleError(collection); - return false; - } - if (collection.size()!=0) { - System.err.println(name+" unsorted size() failed."); - handleError(collection); - return false; - } - - if (validateIterator && collection instanceof java.util.List && !testListIterator(((java.util.List)collection).listIterator())) { - System.err.println(name+" unsorted list iterator failed."); - handleError(collection); - return false; - } - } - - unsortedCount++; - { // UNSORTED: Add in reverse (from index length-1 to zero) order and then remove in order (from index zero to length) - beforeMemory = 0L; - afterMemory = 0L; - beforeAddTime = 0L; - afterAddTime = 0L; - if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); - if (debugTime) beforeAddTime = System.nanoTime(); - for (int i = unsorted.length - 1; i >= 0; i--) { - T item = null; - if (type==Type.Integer) { - item = (T)unsorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(unsorted[i]); - } - boolean added = collection.add(item); - if (!added) { - System.err.println(name+" unsorted add failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterAddTime = System.nanoTime(); - addTime += afterAddTime - beforeAddTime; - if (debug > 0) System.out.println(name+" unsorted add time = " + (addTime / unsortedCount) + " ns"); - } - if (debugMemory) { - afterMemory = DataStructures.getMemoryUse(); - memory += afterMemory - beforeMemory; - if (debug > 0) System.out.println(name+" unsorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); - } - - if (debug > 1) System.out.println(collection.toString()); - - if (validateIterator && !testIterator(collection.iterator())) { - System.err.println(name+" unsorted iterator failed."); - handleError(collection); - return false; - } - - beforeLookupTime = 0L; - afterLookupTime = 0L; - if (debugTime) beforeLookupTime = System.nanoTime(); - for (int i = 0; i < unsorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)unsorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(unsorted[i]); - } - boolean contains = collection.contains(item); - if (!contains) { - System.err.println(name+" unsorted contains failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterLookupTime = System.nanoTime(); - lookupTime += afterLookupTime - beforeLookupTime; - if (debug > 0) System.out.println(name+" unsorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); - } - - beforeRemoveTime = 0L; - afterRemoveTime = 0L; - if (debugTime) beforeRemoveTime = System.nanoTime(); - for (int i = 0; i < unsorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)unsorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(unsorted[i]); - } - boolean removed = collection.remove(item); - if (!removed) { - System.err.println(name+" unsorted remove failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterRemoveTime = System.nanoTime(); - removeTime += afterRemoveTime - beforeRemoveTime; - if (debug > 0) System.out.println(name+" unsorted remove time = " + (removeTime / unsortedCount) + " ns"); - } - - if (!collection.isEmpty()) { - System.err.println(name+" unsorted isEmpty() failed."); - handleError(collection); - return false; - } - if (collection.size()!=0) { - System.err.println(name+" unsorted size() failed."); - handleError(collection); - return false; - } - } - - long addSortedTime = 0L; - long removeSortedTime = 0L; - - long beforeAddSortedTime = 0L; - long afterAddSortedTime = 0L; - - long beforeRemoveSortedTime = 0L; - long afterRemoveSortedTime = 0L; - - sortedCount++; - { // SORTED: Add and remove in order (from index zero to length) - beforeMemory = 0L; - afterMemory = 0L; - beforeAddSortedTime = 0L; - afterAddSortedTime = 0L; - if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); - if (debugTime) beforeAddSortedTime = System.nanoTime(); - for (int i = 0; i < sorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)sorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(sorted[i]); - } - boolean added = collection.add(item); - if (!added) { - System.err.println(name+" sorted add failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterAddSortedTime = System.nanoTime(); - addSortedTime += afterAddSortedTime - beforeAddSortedTime; - if (debug > 0) System.out.println(name+" sorted add time = " + (addSortedTime / sortedCount) + " ns"); - } - if (debugMemory) { - afterMemory = DataStructures.getMemoryUse(); - memory += afterMemory - beforeMemory; - if (debug > 0) System.out.println(name+" sorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); - } - - if (debug > 1) System.out.println(collection.toString()); - - if (validateIterator && !testIterator(collection.iterator())) { - System.err.println(name+" sorted iterator failed."); - handleError(collection); - return false; - } - - beforeLookupTime = 0L; - afterLookupTime = 0L; - if (debugTime) beforeLookupTime = System.nanoTime(); - for (int i = 0; i < unsorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)sorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(sorted[i]); - } - boolean contains = collection.contains(item); - if (!contains) { - System.err.println(name+" sorted contains failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterLookupTime = System.nanoTime(); - lookupTime += afterLookupTime - beforeLookupTime; - if (debug > 0) System.out.println(name+" sorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); - } - - beforeRemoveSortedTime = 0L; - afterRemoveSortedTime = 0L; - if (debugTime) beforeRemoveSortedTime = System.nanoTime(); - for (int i = 0; i < sorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)sorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(sorted[i]); - } - boolean removed = collection.remove(item); - if (!removed) { - System.err.println(name+" sorted remove failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterRemoveSortedTime = System.nanoTime(); - removeSortedTime += afterRemoveSortedTime - beforeRemoveSortedTime; - if (debug > 0) System.out.println(name+" sorted remove time = " + (removeSortedTime / sortedCount) + " ns"); - } - - if (!collection.isEmpty()) { - System.err.println(name+" sorted isEmpty() failed."); - handleError(collection); - return false; - } - if (collection.size()!=0) { - System.err.println(name+" sorted size() failed."); - handleError(collection); - return false; - } - - if (validateIterator && collection instanceof java.util.List && !testListIterator(((java.util.List)collection).listIterator())) { - System.err.println(name+" sorted list iterator failed."); - handleError(collection); - return false; - } - } - - sortedCount++; - { // SORTED: Add in order (from index zero to length) and then remove in reverse (from index length-1 to zero) order - beforeMemory = 0L; - afterMemory = 0L; - beforeAddSortedTime = 0L; - afterAddSortedTime = 0L; - if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); - if (debugTime) beforeAddSortedTime = System.nanoTime(); - for (int i = 0; i < sorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)sorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(sorted[i]); - } - boolean added = collection.add(item); - if (!added) { - System.err.println(name+" sorted add failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterAddSortedTime = System.nanoTime(); - addSortedTime += afterAddSortedTime - beforeAddSortedTime; - if (debug > 0) System.out.println(name+" sorted add time = " + (addSortedTime / sortedCount) + " ns"); - } - if (debugMemory) { - afterMemory = DataStructures.getMemoryUse(); - memory += afterMemory - beforeMemory; - if (debug > 0) System.out.println(name+" sorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); - } - - if (debug > 1) System.out.println(collection.toString()); - - if (validateIterator && !testIterator(collection.iterator())) { - System.err.println(name+" sorted iterator failed."); - handleError(collection); - return false; - } - - beforeLookupTime = 0L; - afterLookupTime = 0L; - if (debugTime) beforeLookupTime = System.nanoTime(); - for (int i = 0; i < unsorted.length; i++) { - T item = null; - if (type==Type.Integer) { - item = (T)sorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(sorted[i]); - } - boolean contains = collection.contains(item); - if (!contains) { - System.err.println(name+" sorted contains failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterLookupTime = System.nanoTime(); - lookupTime += afterLookupTime - beforeLookupTime; - if (debug > 0) System.out.println(name+" sorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); - } - - beforeRemoveSortedTime = 0L; - afterRemoveSortedTime = 0L; - if (debugTime) beforeRemoveSortedTime = System.nanoTime(); - for (int i = sorted.length - 1; i >= 0; i--) { - T item = null; - if (type==Type.Integer) { - item = (T)sorted[i]; - } else if (type==Type.String) { - item = (T)String.valueOf(sorted[i]); - } - boolean removed = collection.remove(item); - if (!removed) { - System.err.println(name+" sorted remove failed."); - handleError(collection); - return false; - } - } - if (debugTime) { - afterRemoveSortedTime = System.nanoTime(); - removeSortedTime += afterRemoveSortedTime - beforeRemoveSortedTime; - if (debug > 0) System.out.println(name+" sorted remove time = " + (removeSortedTime / sortedCount) + " ns"); - } - - if (!collection.isEmpty()) { - System.err.println(name+" sorted isEmpty() failed."); - handleError(collection); - return false; - } - if (collection.size()!=0) { - System.err.println(name+" sorted size() failed."); - handleError(collection); - return false; - } - - if (validateIterator && collection instanceof java.util.List && !testListIterator(((java.util.List)collection).listIterator())) { - System.err.println(name+" sorted list iterator failed."); - handleError(collection); - return false; - } - } - - if (testResults[testIndex] == null) testResults[testIndex] = new long[6]; - testResults[testIndex][0] += addTime / unsortedCount; - testResults[testIndex][1] += removeTime / unsortedCount; - testResults[testIndex][2] += addSortedTime / sortedCount; - testResults[testIndex][3] += removeSortedTime / sortedCount; - testResults[testIndex][4] += lookupTime / (unsortedCount + sortedCount); - testResults[testIndex++][5] += memory / (unsortedCount + sortedCount); - - if (debug > 1) System.out.println(); - - return true; - } - - private static boolean testJavaMap(java.util.Map map, Type keyType, String name) { - // Make sure the map is empty - if (!map.isEmpty()) { - System.err.println(name+" initial isEmpty() failed."); - handleError(map); - return false; - } - if (map.size()!=0) { - System.err.println(name+" initial size() failed."); - handleError(map); - return false; - } - - long sortedCount = 0; - long unsortedCount = 0; - - long addTime = 0L; - long removeTime = 0L; - - long beforeAddTime = 0L; - long afterAddTime = 0L; - long beforeRemoveTime = 0L; - long afterRemoveTime = 0L; - - long memory = 0L; - - long beforeMemory = 0L; - long afterMemory = 0L; - - long lookupTime = 0L; - - long beforeLookupTime = 0L; - long afterLookupTime = 0L; - - if (debug > 1) System.out.println(name); - testNames[testIndex] = name; - - unsortedCount++; - { - beforeMemory = 0L; - afterMemory = 0L; - beforeAddTime = 0L; - afterAddTime = 0L; - if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); - if (debugTime) beforeAddTime = System.nanoTime(); - for (int i = 0; i < unsorted.length; i++) { - Integer item = unsorted[i]; - K k = null; - V v = null; - if (keyType == Type.Integer) { - k = (K)item; - v = (V)String.valueOf(item); - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - v = (V)item; - } - map.put(k, v); - } - if (debugTime) { - afterAddTime = System.nanoTime(); - addTime += afterAddTime - beforeAddTime; - if (debug > 0) System.out.println(name+" unsorted add time = " + (addTime / unsortedCount) + " ns"); - } - if (debugMemory) { - afterMemory = DataStructures.getMemoryUse(); - memory += afterMemory - beforeMemory; - if (debug > 0) System.out.println(name+" unsorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); - } - - K invalidKey = null; - if (keyType == Type.Integer) { - invalidKey = (K)INVALID; - } else if (keyType == Type.String) { - invalidKey = (K)String.valueOf(INVALID); - } - boolean contains = map.containsKey(invalidKey); - V removed = map.remove(invalidKey); - if (contains || (removed!=null)) { - System.err.println(name+" unsorted invalidity check. contains=" + contains + " removed=" + removed); - return false; - } - - if (debug > 1) System.out.println(map.toString()); - - beforeLookupTime = 0L; - afterLookupTime = 0L; - if (debugTime) beforeLookupTime = System.nanoTime(); - for (Integer item : unsorted) { - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - map.containsKey(k); - } - if (debugTime) { - afterLookupTime = System.nanoTime(); - lookupTime += afterLookupTime - beforeLookupTime; - if (debug > 0) System.out.println(name+" unsorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); - } - - if (debugTime) beforeRemoveTime = System.nanoTime(); - for (int i = 0; i < unsorted.length; i++) { - Integer item = unsorted[i]; - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - removed = map.remove(k); - if (removed==null) { - System.err.println(name+" unsorted invalidity check. removed=" + removed); - return false; - } - - } - if (debugTime) { - afterRemoveTime = System.nanoTime(); - removeTime += afterRemoveTime - beforeRemoveTime; - if (debug > 0) System.out.println(name+" unsorted remove time = " + (removeTime / unsortedCount) + " ns"); - } - - if (validateIterator && !testMapEntrySet(map,keyType)) return false; - - if (!map.isEmpty()) { - System.err.println(name+" unsorted isEmpty() failed."); - handleError(map); - return false; - } - if (map.size()!=0) { - System.err.println(name+" unsorted size() failed."); - handleError(map); - return false; - } - } - - unsortedCount++; - { - beforeMemory = 0L; - afterMemory = 0L; - beforeAddTime = 0L; - afterAddTime = 0L; - if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); - if (debugTime) beforeAddTime = System.nanoTime(); - for (int i = unsorted.length - 1; i >= 0; i--) { - Integer item = unsorted[i]; - K k = null; - V v = null; - if (keyType == Type.Integer) { - k = (K)item; - v = (V)String.valueOf(item); - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - v = (V)item; - } - map.put(k, v); - } - if (debugTime) { - afterAddTime = System.nanoTime(); - addTime += afterAddTime - beforeAddTime; - if (debug > 0) System.out.println(name+" unsorted add time = " + (addTime / unsortedCount) + " ns"); - } - if (debugMemory) { - afterMemory = DataStructures.getMemoryUse(); - memory += afterMemory - beforeMemory; - if (debug > 0) System.out.println(name+" unsorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); - } - - K invalidKey = null; - if (keyType == Type.Integer) { - invalidKey = (K)INVALID; - } else if (keyType == Type.String) { - invalidKey = (K)String.valueOf(INVALID); - } - boolean contains = map.containsKey(invalidKey); - V removed = map.remove(invalidKey); - if (contains || (removed!=null)) { - System.err.println(name+" unsorted invalidity check. contains=" + contains + " removed=" + removed); - return false; - } - - if (debug > 1) System.out.println(map.toString()); - - beforeLookupTime = 0L; - afterLookupTime = 0L; - if (debugTime) beforeLookupTime = System.nanoTime(); - for (Integer item : unsorted) { - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - map.containsKey(k); - } - if (debugTime) { - afterLookupTime = System.nanoTime(); - lookupTime += afterLookupTime - beforeLookupTime; - if (debug > 0) System.out.println(name+" unsorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); - } - - beforeRemoveTime = 0L; - afterRemoveTime = 0L; - if (debugTime) beforeRemoveTime = System.nanoTime(); - for (int i = unsorted.length - 1; i >= 0; i--) { - Integer item = unsorted[i]; - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - removed = map.remove(k); - if (removed==null) { - System.err.println(name+" unsorted invalidity check. removed=" + removed); - return false; - } - } - if (debugTime) { - afterRemoveTime = System.nanoTime(); - removeTime += afterRemoveTime - beforeRemoveTime; - if (debug > 0) System.out.println(name+" unsorted remove time = " + (removeTime / unsortedCount) + " ns"); - } - - if (!map.isEmpty()) { - System.err.println(name+" unsorted isEmpty() failed."); - handleError(map); - return false; - } - if (map.size()!=0) { - System.err.println(name+" unsorted size() failed."); - handleError(map); - return false; - } - } - - long addSortedTime = 0L; - long removeSortedTime = 0L; - - long beforeAddSortedTime = 0L; - long afterAddSortedTime = 0L; - - long beforeRemoveSortedTime = 0L; - long afterRemoveSortedTime = 0L; - - sortedCount++; - { // sorted - beforeMemory = 0L; - afterMemory = 0L; - beforeAddSortedTime = 0L; - afterAddSortedTime = 0L; - if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); - if (debugTime) beforeAddSortedTime = System.nanoTime(); - for (int i = 0; i < sorted.length; i++) { - Integer item = sorted[i]; - K k = null; - V v = null; - if (keyType == Type.Integer) { - k = (K)item; - v = (V)String.valueOf(item); - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - v = (V)item; - } - map.put(k, v); - } - if (debugTime) { - afterAddSortedTime = System.nanoTime(); - addSortedTime += afterAddSortedTime - beforeAddSortedTime; - if (debug > 0) System.out.println(name+" sorted add time = " + (addSortedTime / sortedCount) + " ns"); - } - if (debugMemory) { - afterMemory = DataStructures.getMemoryUse(); - memory += afterMemory - beforeMemory; - if (debug > 0) System.out.println(name+" sorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); - } - - K invalidKey = null; - if (keyType == Type.Integer) { - invalidKey = (K)INVALID; - } else if (keyType == Type.String) { - invalidKey = (K)String.valueOf(INVALID); - } - boolean contains = map.containsKey(invalidKey); - V removed = map.remove(invalidKey); - if (contains || (removed!=null)) { - System.err.println(name+" sorted invalidity check. contains=" + contains + " removed=" + removed); - return false; - } - - if (debug > 1) System.out.println(map.toString()); - - beforeLookupTime = 0L; - afterLookupTime = 0L; - if (debugTime) beforeLookupTime = System.nanoTime(); - for (Integer item : sorted) { - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - map.containsKey(k); - } - if (debugTime) { - afterLookupTime = System.nanoTime(); - lookupTime += afterLookupTime - beforeLookupTime; - if (debug > 0) System.out.println(name+" sorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); - } - - beforeRemoveSortedTime = 0L; - afterRemoveSortedTime = 0L; - if (debugTime) beforeRemoveSortedTime = System.nanoTime(); - for (int i = 0; i < sorted.length; i++) { - Integer item = sorted[i]; - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - removed = map.remove(k); - if (removed==null) { - System.err.println(name+" unsorted invalidity check. removed=" + removed); - return false; - } - } - if (debugTime) { - afterRemoveSortedTime = System.nanoTime(); - removeSortedTime += afterRemoveSortedTime - beforeRemoveSortedTime; - if (debug > 0) System.out.println(name+" sorted remove time = " + (removeSortedTime / sortedCount) + " ns"); - } - - if (validateIterator && !testMapEntrySet(map,keyType)) return false; - - if (!map.isEmpty()) { - System.err.println(name+" sorted isEmpty() failed."); - handleError(map); - return false; - } - if (map.size()!=0) { - System.err.println(name+" sorted size() failed."); - handleError(map); - return false; - } - } - - sortedCount++; - { // sorted - beforeMemory = 0L; - afterMemory = 0L; - beforeAddSortedTime = 0L; - afterAddSortedTime = 0L; - if (debugMemory) beforeMemory = DataStructures.getMemoryUse(); - if (debugTime) beforeAddSortedTime = System.nanoTime(); - for (int i = 0; i < sorted.length; i++) { - Integer item = sorted[i]; - K k = null; - V v = null; - if (keyType == Type.Integer) { - k = (K)item; - v = (V)String.valueOf(item); - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - v = (V)item; - } - map.put(k, v); - } - if (debugTime) { - afterAddSortedTime = System.nanoTime(); - addSortedTime += afterAddSortedTime - beforeAddSortedTime; - if (debug > 0) System.out.println(name+" sorted add time = " + (addSortedTime / sortedCount) + " ns"); - } - if (debugMemory) { - afterMemory = DataStructures.getMemoryUse(); - memory += afterMemory - beforeMemory; - if (debug > 0) System.out.println(name+" sorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); - } - - K invalidKey = null; - if (keyType == Type.Integer) { - invalidKey = (K)INVALID; - } else if (keyType == Type.String) { - invalidKey = (K)String.valueOf(INVALID); - } - boolean contains = map.containsKey(invalidKey); - V removed = map.remove(invalidKey); - if (contains || (removed!=null)) { - System.err.println(name+" sorted invalidity check. contains=" + contains + " removed=" + removed); - return false; - } - - if (debug > 1) System.out.println(map.toString()); - - beforeLookupTime = 0L; - afterLookupTime = 0L; - if (debugTime) beforeLookupTime = System.nanoTime(); - for (Integer item : sorted) { - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - map.containsKey(k); - } - if (debugTime) { - afterLookupTime = System.nanoTime(); - lookupTime += afterLookupTime - beforeLookupTime; - if (debug > 0) System.out.println(name+" sorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); - } - - beforeRemoveSortedTime = 0L; - afterRemoveSortedTime = 0L; - if (debugTime) beforeRemoveSortedTime = System.nanoTime(); - for (int i = sorted.length - 1; i >= 0; i--) { - Integer item = sorted[i]; - K k = null; - if (keyType == Type.Integer) { - k = (K)item; - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - } - removed = map.remove(k); - if (removed==null) { - System.err.println(name+" unsorted invalidity check. removed=" + removed); - return false; - } - } - if (debugTime) { - afterRemoveSortedTime = System.nanoTime(); - removeSortedTime += afterRemoveSortedTime - beforeRemoveSortedTime; - if (debug > 0) System.out.println(name+" sorted remove time = " + (removeSortedTime / sortedCount) + " ns"); - } - - if (validateIterator && !testMapEntrySet(map,keyType)) return false; - - if (!map.isEmpty()) { - System.err.println(name+" sorted isEmpty() failed."); - handleError(map); - return false; - } - if (map.size()!=0) { - System.err.println(name+" sorted size() failed."); - handleError(map); - return false; - } - - } - - if (testResults[testIndex] == null) testResults[testIndex] = new long[6]; - testResults[testIndex][0] += addTime / unsortedCount; - testResults[testIndex][1] += removeTime / unsortedCount; - testResults[testIndex][2] += addSortedTime / sortedCount; - testResults[testIndex][3] += removeSortedTime / sortedCount; - testResults[testIndex][4] += lookupTime / (unsortedCount + sortedCount); - testResults[testIndex++][5] += memory / (unsortedCount + sortedCount); - - if (debug > 1) System.out.println(); - - return true; - } - - private static boolean testMapEntrySet(java.util.Map map, Type keyType) { - { // Test keys - for (int i = 0; i < sorted.length; i++) { - Integer item = sorted[i]; - K k = null; - V v = null; - if (keyType == Type.Integer) { - k = (K)item; - v = (V)String.valueOf(item); - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - v = (V)item; - } - map.put(k, v); - } - - java.util.Set set = map.keySet(); - for (int i = 0; i < sorted.length; i++) { - Integer key = sorted[i]; - K k = null; - if (keyType == Type.Integer) { - k = (K)key; - } else if (keyType == Type.String) { - k = (K)String.valueOf(key); - } - if (!set.contains(k)) { - System.err.println("MayEntry contains() failure."); - handleError(map); - return false; - } - } - - java.util.Iterator keyIter = set.iterator(); - while (keyIter.hasNext()) { - keyIter.next(); - keyIter.remove(); - } - - if (!map.isEmpty()) { - System.err.println("MayEntry isEmpty() failure."); - handleError(map); - return false; - } - if (map.size()!=0) { - System.err.println("MayEntry size()!=0 failure."); - handleError(map); - return false; - } - } - - { // Test values - for (int i = 0; i < sorted.length; i++) { - Integer item = sorted[i]; - K k = null; - V v = null; - if (keyType == Type.Integer) { - k = (K)item; - v = (V)String.valueOf(item); - } else if (keyType == Type.String) { - k = (K)String.valueOf(item); - v = (V)item; - } - map.put(k, v); - } - - java.util.Collection collection = map.values(); - for (int i = 0; i < sorted.length; i++) { - Integer value = sorted[i]; - V v = null; - // These are reversed on purpose - if (keyType == Type.Integer) { - v = (V)String.valueOf(value); - } else if (keyType == Type.String) { - v = (V)value; - } - if (!collection.contains(v)) { - System.err.println("MayEntry contains() failure."); - handleError(map); - return false; - } - } - - java.util.Iterator valueIter = collection.iterator(); - while (valueIter.hasNext()) { - valueIter.next(); - valueIter.remove(); - } - - if (!map.isEmpty()) { - System.err.println("MayEntry isEmpty() failure."); - handleError(map); - return false; - } - if (map.size()!=0) { - System.err.println("MayEntry size()!=0 failure."); - handleError(map); - return false; - } - } - return true; - } - - private static > boolean testIterator(Iterator iter) { - while (iter.hasNext()) { - T item = iter.next(); - if (item==null) { - System.err.println("Iterator failure."); - return false; - } - } - return true; - } - - private static > boolean testListIterator(ListIterator iter) { - // Make sure you catch going prev at the start - boolean exceptionThrown = false; - try { - iter.previous(); - } catch (NoSuchElementException e) { - exceptionThrown = true; - } - if (!exceptionThrown) { - System.err.println("ListIterator exception failure."); - return false; - } - - for (int i = 0; i < unsorted.length; i++) { - T t = (T)unsorted[i]; - iter.add(t); - } - while (iter.hasPrevious()) iter.previous(); - - int i = 0; - while (iter.hasNext()) { - T item = iter.next(); - int idx = iter.nextIndex(); - if (idx!=++i) { - System.err.println("ListIterator index failure."); - return false; - } - if (item==null) { - System.err.println("ListIterator item is null."); - return false; - } - } - - // We should be at the end of the collection, this should fail - exceptionThrown = false; - try { - iter.next(); - } catch (NoSuchElementException e) { - exceptionThrown = true; - } - if (!exceptionThrown) { - System.err.println("ListIterator exception failure."); - return false; - } - - //This should be list.size - iter.nextIndex(); - int listSize = iter.nextIndex(); - if (listSize!=ARRAY_SIZE) { - System.err.println("ListIterator ARRAY_SIZE failure."); - return false; - } - - i--; - while (iter.hasPrevious()) { - T item = iter.previous(); - int idx = iter.previousIndex(); - if (idx!=--i) { - System.err.println("ListIterator index failure."); - return false; - } - if (item==null) { - System.err.println("ListIterator item is null."); - return false; - } - } - - // We should be at the beginning of the collection, this should fail - exceptionThrown = false; - try { - iter.previous(); - } catch (NoSuchElementException e) { - exceptionThrown = true; - } - if (!exceptionThrown) { - System.err.println("ListIterator exception failure."); - return false; - } - - // This should be negative one - iter.previousIndex(); - int negOne = iter.previousIndex(); - if (negOne!=-1) { - System.err.println("ListIterator negative_one failure."); - return false; - } - - // Remove all using iterator - while (iter.hasNext()) { - iter.next(); - iter.remove(); - } - - return true; - } - - private static final String getTestResults(int testNumber, String[] names, long[][] results) { - StringBuilder resultsBuilder = new StringBuilder(); - String format = "%-32s %-10s %-15s %-15s %-20s %-15s %-15s\n"; - Formatter formatter = new Formatter(resultsBuilder, Locale.US); - formatter.format(format, "Data Structure", "Add time", "Remove time", "Sorted add time", "Sorted remove time", "Lookup time", "Size"); - - double KB = 1000; - double MB = 1000 * KB; - - double MILLIS = 1000000; - double SECOND = 1000; - double MINUTES = 60 * SECOND; - - for (int i=0; i MINUTES) { - addTime /= MINUTES; - addTimeString = FORMAT.format(addTime) + " m"; - } else if (addTime > SECOND) { - addTime /= SECOND; - addTimeString = FORMAT.format(addTime) + " s"; - } else { - addTimeString = FORMAT.format(addTime) + " ms"; - } - - double removeTime = result[1] / MILLIS; - removeTime /= testNumber; - String removeTimeString = null; - if (removeTime > MINUTES) { - removeTime /= MINUTES; - removeTimeString = FORMAT.format(removeTime) + " m"; - } else if (removeTime > SECOND) { - removeTime /= SECOND; - removeTimeString = FORMAT.format(removeTime) + " s"; - } else { - removeTimeString = FORMAT.format(removeTime) + " ms"; - } - - // sorted - double addSortedTime = result[2] / MILLIS; - addSortedTime /= testNumber; - String sortedAddTimeString = null; - if (addSortedTime > MINUTES) { - addSortedTime /= MINUTES; - sortedAddTimeString = FORMAT.format(addSortedTime) + " m"; - } else if (addSortedTime > SECOND) { - addSortedTime /= SECOND; - sortedAddTimeString = FORMAT.format(addSortedTime) + " s"; - } else { - sortedAddTimeString = FORMAT.format(addSortedTime) + " ms"; - } - - double removeSortedTime = result[3] / MILLIS; - removeSortedTime /= testNumber; - String sortedRemoveTimeString = null; - if (removeSortedTime > MINUTES) { - removeSortedTime /= MINUTES; - sortedRemoveTimeString = FORMAT.format(removeSortedTime) + " m"; - } else if (removeSortedTime > SECOND) { - removeSortedTime /= SECOND; - sortedRemoveTimeString = FORMAT.format(removeSortedTime) + " s"; - } else { - sortedRemoveTimeString = FORMAT.format(removeSortedTime) + " ms"; - } - - double lookupTime = result[4] / MILLIS; - lookupTime /= testNumber; - String lookupTimeString = null; - if (lookupTime > MINUTES) { - lookupTime /= MINUTES; - lookupTimeString = FORMAT.format(lookupTime) + " m"; - } else if (lookupTime > SECOND) { - lookupTime /= SECOND; - lookupTimeString = FORMAT.format(lookupTime) + " s"; - } else { - lookupTimeString = FORMAT.format(lookupTime) + " ms"; - } - - double size = result[5]; - size /= testNumber; - String sizeString = null; - if (size > MB) { - size = size / MB; - sizeString = FORMAT.format(size) + " MB"; - } else if (size > KB) { - size = size / KB; - sizeString = FORMAT.format(size) + " KB"; - } else { - sizeString = FORMAT.format(size) + " Bytes"; - } - - formatter.format(format, name, addTimeString, removeTimeString, sortedAddTimeString, sortedRemoveTimeString, lookupTimeString, sizeString); - } - } - formatter.close(); - - return resultsBuilder.toString(); - } - - private static final String getPathMapString(Graph.Vertex start, Map, Graph.CostPathPair> map) { - StringBuilder builder = new StringBuilder(); - for (Graph.Vertex v : map.keySet()) { - Graph.CostPathPair pair = map.get(v); - builder.append("From ").append(start.getValue()).append(" to vertex=").append(v.getValue()).append("\n"); - if (pair != null) - builder.append(pair.toString()).append("\n"); - - } - return builder.toString(); - } - - private static final String getPathMapString(Map, Map, Set>>> paths) { - StringBuilder builder = new StringBuilder(); - for (Graph.Vertex v : paths.keySet()) { - Map, Set>> map = paths.get(v); - for (Graph.Vertex v2 : map.keySet()) { - builder.append("From=").append(v.getValue()).append(" to=").append(v2.getValue()).append("\n"); - Set> path = map.get(v2); - builder.append(path).append("\n"); - } - } - return builder.toString(); - } - - private static final String getWeightMapString(Map, Map, Integer>> paths) { - StringBuilder builder = new StringBuilder(); - for (Graph.Vertex v : paths.keySet()) { - Map, Integer> map = paths.get(v); - for (Graph.Vertex v2 : map.keySet()) { - builder.append("From=").append(v.getValue()).append(" to=").append(v2.getValue()).append("\n"); - Integer weight = map.get(v2); - builder.append(weight).append("\n"); - } - } - return builder.toString(); - } - - private static final long getMemoryUse() { - putOutTheGarbage(); - long totalMemory = Runtime.getRuntime().totalMemory(); - - putOutTheGarbage(); - long freeMemory = Runtime.getRuntime().freeMemory(); - - return (totalMemory - freeMemory); - } - - private static final void putOutTheGarbage() { - collectGarbage(); - collectGarbage(); - collectGarbage(); - } - - private static final long fSLEEP_INTERVAL = 100; - - private static final void collectGarbage() { - try { - System.gc(); - Thread.sleep(fSLEEP_INTERVAL); - System.runFinalization(); - Thread.sleep(fSLEEP_INTERVAL); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - } -} diff --git a/src/com/jwetherell/algorithms/Sequences.java b/src/com/jwetherell/algorithms/Sequences.java deleted file mode 100644 index f152d193..00000000 --- a/src/com/jwetherell/algorithms/Sequences.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.jwetherell.algorithms; - -import java.text.DecimalFormat; - -import com.jwetherell.algorithms.sequence.FibonacciSequence; -import com.jwetherell.algorithms.sequence.LongestCommonSubsequence; -import com.jwetherell.algorithms.sequence.TotalOfSequence; - -public class Sequences { - - private static final DecimalFormat FORMAT = new DecimalFormat("#.######"); - - public static void main(String[] args) { - { - // TOTAL OF A SEQUENCE OF NUMBERS - int start = 14; - int length = 10000; - System.out.println("Computing sequence total using a loop."); - long before = System.nanoTime(); - long result = TotalOfSequence.sequenceTotalUsingLoop(start, length); - long after = System.nanoTime(); - System.out.println("start=" + start + " length=" + length + " result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); - System.gc(); - - System.out.println("Computing sequence total using algorithm."); - before = System.nanoTime(); - result = TotalOfSequence.sequenceTotalUsingTriangularNumbers(start, length); - after = System.nanoTime(); - System.out.println("start=" + start + " length=" + length + " result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); - System.out.println(); - System.gc(); - } - - { - // COMPUTE FIBONACCI SEQUENCE - System.out.println("Computing Fibonacci sequence total using a loop."); - int element = 25; - long before = System.nanoTime(); - long result = FibonacciSequence.fibonacciSequenceUsingLoop(element); - long after = System.nanoTime(); - System.out.println("element=" + element + " result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); - System.gc(); - - System.out.println("Computing Fibonacci sequence total using Recursion."); - before = System.nanoTime(); - result = FibonacciSequence.fibonacciSequenceUsingRecursion(element); - after = System.nanoTime(); - System.out.println("element=" + element + " result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); - System.gc(); - - System.out.println("Computing Fibonacci sequence total using Matrix."); - before = System.nanoTime(); - result = FibonacciSequence.fibonacciSequenceUsingMatrixMultiplication(element); - after = System.nanoTime(); - System.out.println("element=" + element + " result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); - System.gc(); - - System.out.println("Computing Fibonacci sequence total using Binet's formula."); - before = System.nanoTime(); - result = FibonacciSequence.fibonacciSequenceUsingBinetsFormula(element); - after = System.nanoTime(); - System.out.println("element=" + element + " result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); - System.out.println(); - System.gc(); - } - - { - // LONGEST COMMON SUBSEQUENCE - System.out.println("Computing longest common subsequence."); - char[] seq1 = new char[] { 'G', 'A', 'C' }; - char[] seq2 = new char[] { 'A', 'G', 'C', 'A', 'T' }; - LongestCommonSubsequence.MatrixPair pair = LongestCommonSubsequence.getLCS(seq1, seq2); - System.out.println(pair.getLengthMatrixString()); - System.out.println(pair.getSequenceMatrixString()); - System.out.println("Longest sequence length = " + pair.getLongestSequenceLength()); - System.out.println("Longest sequences = " + pair.getLongestSequences()); - System.out.println(); - - seq1 = new char[] { 'G', 'A', 'C', 'V', 'X', 'T' }; - seq2 = new char[] { 'A', 'G', 'C', 'A', 'T', 'X' }; - pair = LongestCommonSubsequence.getLCS(seq1, seq2); - System.out.println(pair.getLengthMatrixString()); - System.out.println(pair.getSequenceMatrixString()); - System.out.println("Longest sequence length = " + pair.getLongestSequenceLength()); - System.out.println("Longest sequences = " + pair.getLongestSequences()); - System.out.println(); - } - } -} diff --git a/src/com/jwetherell/algorithms/Sorts.java b/src/com/jwetherell/algorithms/Sorts.java deleted file mode 100644 index adf79db8..00000000 --- a/src/com/jwetherell/algorithms/Sorts.java +++ /dev/null @@ -1,664 +0,0 @@ -package com.jwetherell.algorithms; - -import java.text.DecimalFormat; -import java.util.Random; - -import com.jwetherell.algorithms.sorts.AmericanFlagSort; -import com.jwetherell.algorithms.sorts.BubbleSort; -import com.jwetherell.algorithms.sorts.CountingSort; -import com.jwetherell.algorithms.sorts.HeapSort; -import com.jwetherell.algorithms.sorts.InsertionSort; -import com.jwetherell.algorithms.sorts.MergeSort; -import com.jwetherell.algorithms.sorts.QuickSort; -import com.jwetherell.algorithms.sorts.RadixSort; -import com.jwetherell.algorithms.sorts.ShellSort; - -public class Sorts { - - private static final DecimalFormat FORMAT = new DecimalFormat("#.###"); - private static final int SIZE = 10000; - - private static final boolean showResult = false; - private static final boolean showComparison = true; - private static final boolean checkResults = true; - - private static int insertionCount = 0; - private static final double[] insertionResults = new double[1 * 3]; - private static int bubbleCount = 0; - private static final double[] bubbleResults = new double[1 * 3]; - private static int shellCount = 0; - private static final double[] shellResults = new double[1 * 3]; - private static int mergeCount = 0; - private static final double[] mergeResults = new double[1 * 3]; - private static int quickCount = 0; - private static final double[] quickResults = new double[3 * 3]; - private static int heapCount = 0; - private static final double[] heapResults = new double[1 * 3]; - private static int countingCount = 0; - private static final double[] countingResults = new double[1 * 3]; - private static int radixCount = 0; - private static final double[] radixResults = new double[1 * 3]; - private static int americanFlagCount = 0; - private static final double[] americanFlagResults = new double[1 * 3]; - - private static final boolean showInsertion = true; - private static final boolean showBubble = true; - private static final boolean showShell = true; - private static final boolean showMerge = true; - private static final boolean showQuick = true; - private static final boolean showHeap = true; - private static final boolean showCounting = true; - private static final boolean showRadix = true; - private static final boolean showAmericanFlag = true; - - private static Integer[] unsorted = null; - private static Integer[] sorted = null; - private static Integer[] reverse = null; - - public static void main(String[] args) { - System.out.println("Generating random array."); - Random random = new Random(); - unsorted = new Integer[SIZE]; - int i = 0; - while (i < unsorted.length) { - int j = random.nextInt(unsorted.length * 10); - unsorted[i++] = j; - } - System.out.println("Generated random array."); - - System.out.println("Generating sorted array."); - sorted = new Integer[SIZE]; - for (i = 0; i < sorted.length; i++) { - sorted[i] = i; - } - System.out.println("Generated sorted array."); - - System.out.println("Generating reverse sorted array."); - reverse = new Integer[SIZE]; - for (i = (reverse.length - 1); i >= 0; i--) { - reverse[i] = (SIZE - 1) - i; - } - System.out.println("Generated reverse sorted array."); - System.out.println(); - System.out.flush(); - - System.out.println("Starting sorts..."); - System.out.println(); - System.out.flush(); - if (showInsertion) { - // Insertion sort - long bInsertion = System.nanoTime(); - Integer[] result = InsertionSort.sort(unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("InsertionSort failed."); - long aInsertion = System.nanoTime(); - double diff = (aInsertion - bInsertion) / 1000000d / 1000d; - System.out.println("Random: InsertionSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - insertionResults[insertionCount++] = diff; - System.gc(); - - bInsertion = System.nanoTime(); - result = InsertionSort.sort(sorted.clone()); - if (checkResults && !check(result)) - System.err.println("InsertionSort failed."); - aInsertion = System.nanoTime(); - diff = (aInsertion - bInsertion) / 1000000d / 1000d; - System.out.println("Sorted: InsertionSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - insertionResults[insertionCount++] = diff; - System.gc(); - - bInsertion = System.nanoTime(); - result = InsertionSort.sort(reverse.clone()); - if (checkResults && !check(result)) - System.err.println("InsertionSort failed."); - aInsertion = System.nanoTime(); - diff = (aInsertion - bInsertion) / 1000000d / 1000d; - System.out.println("Reverse sorted: InsertionSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - insertionResults[insertionCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - } - - if (showBubble) { - // Bubble sort - long bBubble = System.nanoTime(); - Integer[] result = BubbleSort.sort(unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("BubbleSort failed."); - long aBubble = System.nanoTime(); - double diff = (aBubble - bBubble) / 1000000d / 1000d; - System.out.println("Random: BubbleSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - bubbleResults[bubbleCount++] = diff; - System.gc(); - - bBubble = System.nanoTime(); - result = BubbleSort.sort(sorted.clone()); - if (checkResults && !check(result)) - System.err.println("BubbleSort failed."); - aBubble = System.nanoTime(); - diff = (aBubble - bBubble) / 1000000d / 1000d; - System.out.println("Sorted: BubbleSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - bubbleResults[bubbleCount++] = diff; - System.gc(); - - bBubble = System.nanoTime(); - result = BubbleSort.sort(reverse.clone()); - if (checkResults && !check(result)) - System.err.println("BubbleSort failed."); - aBubble = System.nanoTime(); - diff = (aBubble - bBubble) / 1000000d / 1000d; - System.out.println("Reverse sorted: BubbleSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - bubbleResults[bubbleCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - } - - if (showShell) { - int[] shells = new int[] { 10, 5, 3, 1 }; - // Shell's sort - long bShell = System.nanoTime(); - Integer[] result = ShellSort.sort(shells, unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("ShellSort failed."); - long aShell = System.nanoTime(); - double diff = (aShell - bShell) / 1000000d / 1000d; - System.out.println("Random: ShellSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - shellResults[shellCount++] = diff; - System.gc(); - - bShell = System.nanoTime(); - result = ShellSort.sort(shells, sorted.clone()); - if (checkResults && !check(result)) - System.err.println("ShellSort failed."); - aShell = System.nanoTime(); - diff = (aShell - bShell) / 1000000d / 1000d; - System.out.println("Sorted: ShellSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - shellResults[shellCount++] = diff; - System.gc(); - - bShell = System.nanoTime(); - result = ShellSort.sort(shells, reverse.clone()); - if (checkResults && !check(result)) - System.err.println("ShellSort failed."); - aShell = System.nanoTime(); - diff = (aShell - bShell) / 1000000d / 1000d; - System.out.println("Reverse sorted: ShellSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - shellResults[shellCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - } - - if (showMerge) { - // Merge sort - long bMerge = System.nanoTime(); - Integer[] result = MergeSort.sort(unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("MergeSort failed."); - long aMerge = System.nanoTime(); - double diff = (aMerge - bMerge) / 1000000d / 1000d; - System.out.println("Random: MergeSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - mergeResults[mergeCount++] = diff; - System.gc(); - - bMerge = System.nanoTime(); - result = MergeSort.sort(sorted.clone()); - if (checkResults && !check(result)) - System.err.println("MergeSort failed."); - aMerge = System.nanoTime(); - diff = (aMerge - bMerge) / 1000000d / 1000d; - System.out.println("Sorted: MergeSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - mergeResults[mergeCount++] = diff; - System.gc(); - - bMerge = System.nanoTime(); - result = MergeSort.sort(reverse.clone()); - if (checkResults && !check(result)) - System.err.println("MergeSort failed."); - aMerge = System.nanoTime(); - diff = (aMerge - bMerge) / 1000000d / 1000d; - System.out.println("Reverse sorted: MergeSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - mergeResults[mergeCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - } - - if (showQuick) { - // Quicksort - long bQuick = System.nanoTime(); - Integer[] result = QuickSort.sort(QuickSort.PIVOT_TYPE.FIRST, unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("QuickSort failed."); - long aQuick = System.nanoTime(); - double diff = (aQuick - bQuick) / 1000000d / 1000d; - System.out.println("Random: QuickSort first element pivot=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - quickResults[quickCount++] = diff; - System.gc(); - - bQuick = System.nanoTime(); - result = QuickSort.sort(QuickSort.PIVOT_TYPE.FIRST, sorted.clone()); - if (checkResults && !check(result)) - System.err.println("QuickSort failed."); - aQuick = System.nanoTime(); - diff = (aQuick - bQuick) / 1000000d / 1000d; - System.out.println("Sorted: QuickSort first element pivot=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - quickResults[quickCount++] = diff; - System.gc(); - - bQuick = System.nanoTime(); - result = QuickSort.sort(QuickSort.PIVOT_TYPE.FIRST, reverse.clone()); - if (checkResults && !check(result)) - System.err.println("QuickSort failed."); - aQuick = System.nanoTime(); - diff = (aQuick - bQuick) / 1000000d / 1000d; - System.out.println("Reverse sorted: QuickSort first element pivot=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - quickResults[quickCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - - bQuick = System.nanoTime(); - result = QuickSort.sort(QuickSort.PIVOT_TYPE.MIDDLE, unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("QuickSort failed."); - aQuick = System.nanoTime(); - diff = (aQuick - bQuick) / 1000000d / 1000d; - System.out.println("Random: QuickSort middle element pivot=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - quickResults[quickCount++] = diff; - System.gc(); - - bQuick = System.nanoTime(); - result = QuickSort.sort(QuickSort.PIVOT_TYPE.MIDDLE, sorted.clone()); - if (checkResults && !check(result)) - System.err.println("QuickSort failed."); - aQuick = System.nanoTime(); - diff = (aQuick - bQuick) / 1000000d / 1000d; - System.out.println("Sorted: QuickSort middle element pivot=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - quickResults[quickCount++] = diff; - System.gc(); - - bQuick = System.nanoTime(); - result = QuickSort.sort(QuickSort.PIVOT_TYPE.MIDDLE, reverse.clone()); - if (checkResults && !check(result)) - System.err.println("QuickSort failed."); - aQuick = System.nanoTime(); - diff = (aQuick - bQuick) / 1000000d / 1000d; - System.out.println("Reverse sorted: QuickSort middle element pivot=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - quickResults[quickCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - - bQuick = System.nanoTime(); - result = QuickSort.sort(QuickSort.PIVOT_TYPE.RANDOM, unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("Random QuickSort failed."); - aQuick = System.nanoTime(); - diff = (aQuick - bQuick) / 1000000d / 1000d; - System.out.println("Random: Randomized QuickSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - quickResults[quickCount++] = diff; - System.gc(); - - bQuick = System.nanoTime(); - result = QuickSort.sort(QuickSort.PIVOT_TYPE.RANDOM, sorted.clone()); - if (checkResults && !check(result)) - System.err.println("Random QuickSort failed."); - aQuick = System.nanoTime(); - diff = (aQuick - bQuick) / 1000000d / 1000d; - System.out.println("Sorted: Randomized QuickSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - quickResults[quickCount++] = diff; - System.gc(); - - bQuick = System.nanoTime(); - result = QuickSort.sort(QuickSort.PIVOT_TYPE.RANDOM, reverse.clone()); - if (checkResults && !check(result)) - System.err.println("Random QuickSort failed."); - aQuick = System.nanoTime(); - diff = (aQuick - bQuick) / 1000000d / 1000d; - System.out.println("Reverse sorted: Randomized QuickSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - quickResults[quickCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - } - - if (showHeap) { - // Heapsort - long bHeap = System.nanoTime(); - Integer[] result = HeapSort.sort(unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("HeapSort failed."); - long aHeap = System.nanoTime(); - double diff = (aHeap - bHeap) / 1000000d / 1000d; - System.out.println("Random: HeapSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - heapResults[heapCount++] = diff; - System.gc(); - - bHeap = System.nanoTime(); - result = HeapSort.sort(sorted.clone()); - if (checkResults && !check(result)) - System.err.println("HeapSort failed."); - aHeap = System.nanoTime(); - diff = (aHeap - bHeap) / 1000000d / 1000d; - System.out.println("Sorted: HeapSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - heapResults[heapCount++] = diff; - System.gc(); - - bHeap = System.nanoTime(); - result = HeapSort.sort(reverse.clone()); - if (checkResults && !check(result)) - System.err.println("HeapSort failed."); - aHeap = System.nanoTime(); - diff = (aHeap - bHeap) / 1000000d / 1000d; - System.out.println("Reverse sorted: HeapSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - heapResults[heapCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - } - - if (showCounting) { - // Counting sort - long bCounting = System.nanoTime(); - Integer[] result = CountingSort.sort(unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("CountingSort failed."); - long aCounting = System.nanoTime(); - double diff = (aCounting - bCounting) / 1000000d / 1000d; - System.out.println("Random: CountingSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - countingResults[countingCount++] = diff; - System.gc(); - - bCounting = System.nanoTime(); - result = CountingSort.sort(sorted.clone()); - if (checkResults && !check(result)) - System.err.println("CountingSort failed."); - aCounting = System.nanoTime(); - diff = (aCounting - bCounting) / 1000000d / 1000d; - System.out.println("Sorted: CountingSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - countingResults[countingCount++] = diff; - System.gc(); - - bCounting = System.nanoTime(); - result = CountingSort.sort(reverse.clone()); - if (checkResults && !check(result)) - System.err.println("CountingSort failed."); - aCounting = System.nanoTime(); - diff = (aCounting - bCounting) / 1000000d / 1000d; - System.out.println("Reverse sorted: CountingSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - countingResults[countingCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - } - - if (showRadix) { - // Radix sort - long bRadix = System.nanoTime(); - Integer[] result = RadixSort.sort(unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("RadixSort failed."); - long aRadix = System.nanoTime(); - double diff = (aRadix - bRadix) / 1000000d / 1000d; - System.out.println("Random: RadixSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - radixResults[radixCount++] = diff; - System.gc(); - - bRadix = System.nanoTime(); - result = RadixSort.sort(sorted.clone()); - if (checkResults && !check(result)) - System.err.println("RadixSort failed."); - aRadix = System.nanoTime(); - diff = (aRadix - bRadix) / 1000000d / 1000d; - System.out.println("Sorted: RadixSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - radixResults[radixCount++] = diff; - System.gc(); - - bRadix = System.nanoTime(); - result = RadixSort.sort(reverse.clone()); - if (checkResults && !check(result)) - System.err.println("RadixSort failed."); - aRadix = System.nanoTime(); - diff = (aRadix - bRadix) / 1000000d / 1000d; - System.out.println("Reverse sorted: RadixSort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - radixResults[radixCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - } - - if (showAmericanFlag) { - // American Flag sort - long bRadix = System.nanoTime(); - Integer[] result = AmericanFlagSort.sort(unsorted.clone()); - if (checkResults && !check(result)) - System.err.println("AmericanFlag sort failed."); - long aRadix = System.nanoTime(); - double diff = (aRadix - bRadix) / 1000000d / 1000d; - System.out.println("Random: AmericanFlag sort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(unsorted, result); - if (showComparison) - americanFlagResults[americanFlagCount++] = diff; - System.gc(); - - bRadix = System.nanoTime(); - result = AmericanFlagSort.sort(sorted.clone()); - if (checkResults && !check(result)) - System.err.println("AmericanFlag sort failed."); - aRadix = System.nanoTime(); - diff = (aRadix - bRadix) / 1000000d / 1000d; - System.out.println("Sorted: AmericanFlag sort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(sorted, result); - if (showComparison) - americanFlagResults[americanFlagCount++] = diff; - System.gc(); - - bRadix = System.nanoTime(); - result = AmericanFlagSort.sort(reverse.clone()); - if (checkResults && !check(result)) - System.err.println("AmericanFlag sort failed."); - aRadix = System.nanoTime(); - diff = (aRadix - bRadix) / 1000000d / 1000d; - System.out.println("Reverse sorted: AmericanFlag sort=" + FORMAT.format(diff) + " secs"); - if (showResult) - showResult(reverse, result); - if (showComparison) - americanFlagResults[americanFlagCount++] = diff; - System.gc(); - - System.out.println(); - System.out.flush(); - } - - if (showComparison) - showComparison(); - } - - private static final void showComparison() { - System.out.println("Algorithm\t\t\tRandom\tSorted\tReverse Sorted"); - if (showInsertion) { - int i = 0; - System.out.println("Insertion sort\t\t\t" + FORMAT.format(insertionResults[i++]) + "\t" + FORMAT.format(insertionResults[i++]) + "\t" + FORMAT.format(insertionResults[i++])); - } - if (showBubble) { - int i = 0; - System.out.println("Bubble sort\t\t\t" + FORMAT.format(bubbleResults[i++]) + "\t" + FORMAT.format(bubbleResults[i++]) + "\t" + FORMAT.format(bubbleResults[i++])); - } - if (showShell) { - int i = 0; - System.out.println("Shell sort\t\t\t" + FORMAT.format(shellResults[i++]) + "\t" + FORMAT.format(shellResults[i++]) + "\t" + FORMAT.format(shellResults[i++])); - } - if (showMerge) { - int i = 0; - System.out.println("Merge sort\t\t\t" + FORMAT.format(mergeResults[i++]) + "\t" + FORMAT.format(mergeResults[i++]) + "\t" + FORMAT.format(mergeResults[i++])); - } - if (showQuick) { - int i = 0; - System.out.println("Quicksort with first as pivot\t" + FORMAT.format(quickResults[i++]) + "\t" + FORMAT.format(quickResults[i++]) + "\t" + FORMAT.format(quickResults[i++])); - System.out.println("Quicksort with middle as pivot\t" + FORMAT.format(quickResults[i++]) + "\t" + FORMAT.format(quickResults[i++]) + "\t" + FORMAT.format(quickResults[i++])); - System.out.println("Quicksort with random as pivot\t" + FORMAT.format(quickResults[i++]) + "\t" + FORMAT.format(quickResults[i++]) + "\t" + FORMAT.format(quickResults[i++])); - } - if (showHeap) { - int i = 0; - System.out.println("Heap sort\t\t\t" + FORMAT.format(heapResults[i++]) + "\t" + FORMAT.format(heapResults[i++]) + "\t" + FORMAT.format(heapResults[i++])); - } - if (showCounting) { - int i = 0; - System.out.println("Counting sort\t\t\t" + FORMAT.format(countingResults[i++]) + "\t" + FORMAT.format(countingResults[i++]) + "\t" + FORMAT.format(countingResults[i++])); - } - if (showRadix) { - int i = 0; - System.out.println("Radix sort\t\t\t" + FORMAT.format(radixResults[i++]) + "\t" + FORMAT.format(radixResults[i++]) + "\t" + FORMAT.format(radixResults[i++])); - } - if (showAmericanFlag) { - int i = 0; - System.out.println("American Flag sort\t\t" + FORMAT.format(americanFlagResults[i++]) + "\t" + FORMAT.format(americanFlagResults[i++]) + "\t" + FORMAT.format(americanFlagResults[i++])); - } - } - - private static final void showResult(Integer[] unsorted, Integer[] result) { - System.out.println("Unsorted: " + print(unsorted)); - System.out.println("Sorted: " + print(result)); - System.out.flush(); - } - - private static final boolean check(Integer[] array) { - for (int i = 1; i < array.length; i++) { - if (array[i - 1] > array[i]) - return false; - } - return true; - } - - public static final String print(Integer[] array) { - return print(array, 0, array.length); - } - - public static final String print(Integer[] array, int start, int length) { - final Integer[] clone = array.clone(); - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < length; i++) { - int e = clone[start + i]; - builder.append(e + " "); - } - return builder.toString(); - } - - public static final String printWithPivot(Integer[] array, int pivotIndex, int start, int length) { - final Integer[] clone = array.clone(); - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < length; i++) { - int e = clone[start + i]; - if (i == pivotIndex) - builder.append("`" + e + "` "); - else - builder.append(e + " "); - } - return builder.toString(); - } -} diff --git a/src/com/jwetherell/algorithms/data_structures/AVLTree.java b/src/com/jwetherell/algorithms/data_structures/AVLTree.java index ceff11f9..f3b74e3e 100644 --- a/src/com/jwetherell/algorithms/data_structures/AVLTree.java +++ b/src/com/jwetherell/algorithms/data_structures/AVLTree.java @@ -12,22 +12,30 @@ * trees are more rigidly balanced, they are faster than red-black trees for * lookup intensive applications. However, red-black trees are faster for * insertion and removal. - * - * http://en.wikipedia.org/wiki/AVL_tree - * + *

+ * @see AVL Tree (Wikipedia) + *
* @author Justin Wetherell */ -public class AVLTree> extends BinarySearchTree implements BinarySearchTree.INodeCreator { +public class AVLTree> extends BinarySearchTree { private enum Balance { LEFT_LEFT, LEFT_RIGHT, RIGHT_LEFT, RIGHT_RIGHT - }; + } /** * Default constructor. */ public AVLTree() { - this.creator = this; + this.creator = new BinarySearchTree.INodeCreator() { + /** + * {@inheritDoc} + */ + @Override + public BinarySearchTree.Node createNewNode(BinarySearchTree.Node parent, T id) { + return (new AVLNode(parent, id)); + } + }; } /** @@ -44,13 +52,23 @@ public AVLTree(INodeCreator creator) { protected Node addValue(T id) { Node nodeToReturn = super.addValue(id); AVLNode nodeAdded = (AVLNode) nodeToReturn; + nodeAdded.updateHeight(); + balanceAfterInsert(nodeAdded); + nodeAdded = (AVLNode) nodeAdded.parent; while (nodeAdded != null) { + int h1 = nodeAdded.height; + nodeAdded.updateHeight(); balanceAfterInsert(nodeAdded); + + // If height before and after balance is the same, stop going up the tree + int h2 = nodeAdded.height; + if (h1==h2) + break; + nodeAdded = (AVLNode) nodeAdded.parent; } - return nodeToReturn; } @@ -63,38 +81,31 @@ protected Node addValue(T id) { private void balanceAfterInsert(AVLNode node) { int balanceFactor = node.getBalanceFactor(); if (balanceFactor > 1 || balanceFactor < -1) { - AVLNode parent = null; AVLNode child = null; Balance balance = null; if (balanceFactor < 0) { - parent = (AVLNode) node.lesser; - balanceFactor = parent.getBalanceFactor(); - if (balanceFactor < 0) { - child = (AVLNode) parent.lesser; + child = (AVLNode) node.lesser; + balanceFactor = child.getBalanceFactor(); + if (balanceFactor < 0) balance = Balance.LEFT_LEFT; - } else { - child = (AVLNode) parent.greater; + else balance = Balance.LEFT_RIGHT; - } } else { - parent = (AVLNode) node.greater; - balanceFactor = parent.getBalanceFactor(); - if (balanceFactor < 0) { - child = (AVLNode) parent.lesser; + child = (AVLNode) node.greater; + balanceFactor = child.getBalanceFactor(); + if (balanceFactor < 0) balance = Balance.RIGHT_LEFT; - } else { - child = (AVLNode) parent.greater; + else balance = Balance.RIGHT_RIGHT; - } } if (balance == Balance.LEFT_RIGHT) { // Left-Right (Left rotation, right rotation) - rotateLeft(parent); + rotateLeft(child); rotateRight(node); } else if (balance == Balance.RIGHT_LEFT) { // Right-Left (Right rotation, left rotation) - rotateRight(parent); + rotateRight(child); rotateLeft(node); } else if (balance == Balance.LEFT_LEFT) { // Left-Left (Right rotation) @@ -104,9 +115,8 @@ private void balanceAfterInsert(AVLNode node) { rotateLeft(node); } - node.updateHeight(); // New child node - child.updateHeight(); // New child node - parent.updateHeight(); // New Parent node + child.updateHeight(); + node.updateHeight(); } } @@ -117,32 +127,32 @@ private void balanceAfterInsert(AVLNode node) { protected Node removeValue(T value) { // Find node to remove Node nodeToRemoved = this.getNode(value); - if (nodeToRemoved != null) { - // Find the replacement node - Node replacementNode = this.getReplacementNode(nodeToRemoved); - - // Find the parent of the replacement node to re-factor the - // height/balance of the tree - AVLNode nodeToRefactor = null; - if (replacementNode != null) - nodeToRefactor = (AVLNode) replacementNode.parent; - if (nodeToRefactor == null) - nodeToRefactor = (AVLNode) nodeToRemoved.parent; - if (nodeToRefactor != null && nodeToRefactor.equals(nodeToRemoved)) - nodeToRefactor = (AVLNode) replacementNode; - - // Replace the node - replaceNodeWithNode(nodeToRemoved, replacementNode); - - // Re-balance the tree all the way up the tree - if (nodeToRefactor != null) { - while (nodeToRefactor != null) { - nodeToRefactor.updateHeight(); - balanceAfterDelete(nodeToRefactor); - nodeToRefactor = (AVLNode) nodeToRefactor.parent; - } - } + if (nodeToRemoved==null) + return null; + + // Find the replacement node + Node replacementNode = this.getReplacementNode(nodeToRemoved); + + // Find the parent of the replacement node to re-factor the height/balance of the tree + AVLNode nodeToRefactor = null; + if (replacementNode != null) + nodeToRefactor = (AVLNode) replacementNode.parent; + if (nodeToRefactor == null) + nodeToRefactor = (AVLNode) nodeToRemoved.parent; + if (nodeToRefactor != null && nodeToRefactor == nodeToRemoved) + nodeToRefactor = (AVLNode) replacementNode; + + // Replace the node + replaceNodeWithNode(nodeToRemoved, replacementNode); + + // Re-balance the tree all the way up the tree + while (nodeToRefactor != null) { + nodeToRefactor.updateHeight(); + balanceAfterDelete(nodeToRefactor); + + nodeToRefactor = (AVLNode) nodeToRefactor.parent; } + return nodeToRemoved; } @@ -245,14 +255,6 @@ public String toString() { return AVLTreePrinter.getString(this); } - /** - * {@inheritDoc} - */ - @Override - public Node createNewNode(Node parent, T id) { - return (new AVLNode(parent, id)); - } - protected static class AVLNode> extends Node { protected int height = 1; @@ -281,13 +283,13 @@ protected boolean isLeaf() { /** * Updates the height of this node based on it's children. */ - protected void updateHeight() { + protected int updateHeight() { int lesserHeight = 0; - int greaterHeight = 0; if (lesser != null) { AVLNode lesserAVLNode = (AVLNode) lesser; lesserHeight = lesserAVLNode.height; } + int greaterHeight = 0; if (greater != null) { AVLNode greaterAVLNode = (AVLNode) greater; greaterHeight = greaterAVLNode.height; @@ -298,6 +300,7 @@ protected void updateHeight() { } else { height = greaterHeight + 1; } + return height; } /** @@ -309,11 +312,11 @@ protected void updateHeight() { */ protected int getBalanceFactor() { int lesserHeight = 0; - int greaterHeight = 0; if (lesser != null) { AVLNode lesserAVLNode = (AVLNode) lesser; lesserHeight = lesserAVLNode.height; } + int greaterHeight = 0; if (greater != null) { AVLNode greaterAVLNode = (AVLNode) greater; greaterHeight = greaterAVLNode.height; @@ -363,8 +366,7 @@ private static > String getString(AVLNode node, Strin builder.append(getString((AVLNode) children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() >= 1) { - builder.append(getString((AVLNode) children.get(children.size() - 1), prefix - + (isTail ? " " : "│ "), true)); + builder.append(getString((AVLNode) children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } diff --git a/src/com/jwetherell/algorithms/data_structures/BTree.java b/src/com/jwetherell/algorithms/data_structures/BTree.java index 7e4257d4..d930076b 100644 --- a/src/com/jwetherell/algorithms/data_structures/BTree.java +++ b/src/com/jwetherell/algorithms/data_structures/BTree.java @@ -5,6 +5,8 @@ import java.util.Comparator; import java.util.Deque; +import com.jwetherell.algorithms.data_structures.interfaces.ITree; + /** * B-tree is a tree data structure that keeps data sorted and allows searches, * sequential access, insertions, and deletions in logarithmic time. The B-tree @@ -12,9 +14,9 @@ * two children. Unlike self-balancing binary search trees, the B-tree is * optimized for systems that read and write large blocks of data. It is * commonly used in databases and file-systems. - * - * http://en.wikipedia.org/wiki/B-tree - * + *

+ * @see B-Tree (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") @@ -69,25 +71,29 @@ public boolean add(T value) { split(node); break; } - // navigate + // Navigate + + // Lesser or equal T lesser = node.getKey(0); - if (value.compareTo(lesser) < 0) { + if (value.compareTo(lesser) <= 0) { node = node.getChild(0); continue; } - int size = node.numberOfKeys(); - int last = size - 1; + // Greater + int numberOfKeys = node.numberOfKeys(); + int last = numberOfKeys - 1; T greater = node.getKey(last); if (value.compareTo(greater) > 0) { - node = node.getChild(size); + node = node.getChild(numberOfKeys); continue; } + // Search internal nodes for (int i = 1; i < node.numberOfKeys(); i++) { T prev = node.getKey(i - 1); T next = node.getKey(i); - if (value.compareTo(prev) > 0 && value.compareTo(next) < 0) { + if (value.compareTo(prev) > 0 && value.compareTo(next) <= 0) { node = node.getChild(i); break; } @@ -103,12 +109,13 @@ public boolean add(T value) { /** * The node's key size is greater than maxKeySize, split down the middle. * - * @param node + * @param nodeToSplit * to split. */ - private void split(Node node) { - int size = node.numberOfKeys(); - int medianIndex = size / 2; + private void split(Node nodeToSplit) { + Node node = nodeToSplit; + int numberOfKeys = node.numberOfKeys(); + int medianIndex = numberOfKeys / 2; T medianValue = node.getKey(medianIndex); Node left = new Node(null, maxKeySize, maxChildrenSize); @@ -123,7 +130,7 @@ private void split(Node node) { } Node right = new Node(null, maxKeySize, maxChildrenSize); - for (int i = medianIndex + 1; i < size; i++) { + for (int i = medianIndex + 1; i < numberOfKeys; i++) { right.addKey(node.getKey(i)); } if (node.numberOfChildren() > 0) { @@ -259,18 +266,18 @@ private Node getNode(T value) { continue; } - int size = node.numberOfKeys(); - int last = size - 1; + int numberOfKeys = node.numberOfKeys(); + int last = numberOfKeys - 1; T greater = node.getKey(last); if (value.compareTo(greater) > 0) { - if (node.numberOfChildren() > size) - node = node.getChild(size); + if (node.numberOfChildren() > numberOfKeys) + node = node.getChild(numberOfKeys); else node = null; continue; } - for (int i = 0; i < size; i++) { + for (int i = 0; i < numberOfKeys; i++) { T currentValue = node.getKey(i); if (currentValue.compareTo(value) == 0) { return node; @@ -295,11 +302,12 @@ private Node getNode(T value) { /** * Get the greatest valued child from node. * - * @param node + * @param nodeToGet * child with the greatest value. * @return Node child with greatest value. */ - private Node getGreatestNode(Node node) { + private Node getGreatestNode(Node nodeToGet) { + Node node = nodeToGet; while (node.numberOfChildren() > 0) { node = node.getChild(node.numberOfChildren() - 1); } diff --git a/src/com/jwetherell/algorithms/data_structures/BinaryHeap.java b/src/com/jwetherell/algorithms/data_structures/BinaryHeap.java index 61ecf952..08855e28 100644 --- a/src/com/jwetherell/algorithms/data_structures/BinaryHeap.java +++ b/src/com/jwetherell/algorithms/data_structures/BinaryHeap.java @@ -6,6 +6,8 @@ import java.util.Deque; import java.util.List; +import com.jwetherell.algorithms.data_structures.interfaces.IHeap; + /** * A binary heap is a heap data structure created using a binary tree. It can be * seen as a binary tree with two additional constraints: 1) The shape property: @@ -14,9 +16,9 @@ * the tree is not complete, the nodes of that level are filled from left to * right. 2) The heap property: each node is right than or equal to each of its * children according to a comparison predicate defined for the data structure. - * - * http://en.wikipedia.org/wiki/Binary_heap - * + *

+ * @see Binary Heap (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") @@ -24,11 +26,11 @@ public interface BinaryHeap> extends IHeap { public enum HeapType { Tree, Array - }; + } public enum Type { MIN, MAX - }; + } /** * Get the heap in array form. @@ -44,7 +46,7 @@ public enum Type { */ public static class BinaryHeapArray> implements BinaryHeap { - private static final int MINIMUM_SIZE = 10; + private static final int MINIMUM_SIZE = 1024; private Type type = Type.MIN; private int size = 0; @@ -117,10 +119,8 @@ public int size() { */ @Override public boolean add(T value) { - int growSize = this.size; - if (size >= array.length) { - array = Arrays.copyOf(array, (growSize + (growSize>>1))); - } + if (size >= array.length) + grow(); array[size] = value; heapUp(size++); @@ -128,25 +128,6 @@ public boolean add(T value) { return true; } - protected void heapUp(int nodeIndex) { - T value = this.array[nodeIndex]; - while (nodeIndex >= 0) { - int parentIndex = getParentIndex(nodeIndex); - if (parentIndex < 0) break; - T parent = this.array[parentIndex]; - - if ((type == Type.MIN && parent != null && value.compareTo(parent) < 0) - || (type == Type.MAX && parent != null && value.compareTo(parent) > 0) - ) { - // Node is less than parent, switch node with parent - this.array[parentIndex] = value; - this.array[nodeIndex] = parent; - } else { - nodeIndex = parentIndex; - } - } - } - /** * {@inheritDoc} */ @@ -169,14 +150,112 @@ private T remove(int index) { heapDown(index); - int shrinkSize = size; - if (size >= MINIMUM_SIZE && size < (shrinkSize + (shrinkSize<<1))) { - System.arraycopy(array, 0, array, 0, size); - } + int shrinkSize = array.length>>1; + if (shrinkSize >= MINIMUM_SIZE && size < shrinkSize) + shrink(); return t; } + protected void heapUp(int idx) { + int nodeIndex = idx; + T value = this.array[nodeIndex]; + if (value==null) + return; + + while (nodeIndex >= 0) { + int parentIndex = getParentIndex(nodeIndex); + if (parentIndex < 0) + return; + + T parent = this.array[parentIndex]; + + if ((type == Type.MIN && value.compareTo(parent) < 0) + || (type == Type.MAX && value.compareTo(parent) > 0) + ) { + // Node is greater/lesser than parent, switch node with parent + this.array[parentIndex] = value; + this.array[nodeIndex] = parent; + } else { + return; + } + nodeIndex = parentIndex; + } + } + + protected void heapDown(int index) { + T value = this.array[index]; + if (value==null) + return; + + int leftIndex = getLeftIndex(index); + int rightIndex = getRightIndex(index); + T left = (leftIndex != Integer.MIN_VALUE && leftIndex < this.size) ? this.array[leftIndex] : null; + T right = (rightIndex != Integer.MIN_VALUE && rightIndex < this.size) ? this.array[rightIndex] : null; + + if (left == null && right == null) { + // Nothing to do here + return; + } + + T nodeToMove = null; + int nodeToMoveIndex = -1; + if ((type == Type.MIN && left != null && right != null && value.compareTo(left) > 0 && value.compareTo(right) > 0) + || (type == Type.MAX && left != null && right != null && value.compareTo(left) < 0 && value.compareTo(right) < 0)) { + // Both children are greater/lesser than node + if ((right!=null) && + ((type == Type.MIN && (right.compareTo(left) < 0)) || ((type == Type.MAX && right.compareTo(left) > 0))) + ) { + // Right is greater/lesser than left + nodeToMove = right; + nodeToMoveIndex = rightIndex; + } else if ((left!=null) && + ((type == Type.MIN && left.compareTo(right) < 0) || (type == Type.MAX && left.compareTo(right) > 0)) + ) { + // Left is greater/lesser than right + nodeToMove = left; + nodeToMoveIndex = leftIndex; + } else { + // Both children are equal, use right + nodeToMove = right; + nodeToMoveIndex = rightIndex; + } + } else if ((type == Type.MIN && right != null && value.compareTo(right) > 0) + || (type == Type.MAX && right != null && value.compareTo(right) < 0) + ) { + // Right is greater/lesser than node + nodeToMove = right; + nodeToMoveIndex = rightIndex; + } else if ((type == Type.MIN && left != null && value.compareTo(left) > 0) + || (type == Type.MAX && left != null && value.compareTo(left) < 0) + ) { + // Left is greater/lesser than node + nodeToMove = left; + nodeToMoveIndex = leftIndex; + } + // No node to move, stop recursion + if (nodeToMove == null) + return; + + // Re-factor heap sub-tree + this.array[nodeToMoveIndex] = value; + this.array[index] = nodeToMove; + + heapDown(nodeToMoveIndex); + } + + // Grow the array by 50% + private void grow() { + int growSize = size + (size<<1); + array = Arrays.copyOf(array, growSize); + } + + // Shrink the array by 50% + private void shrink() { + int shrinkSize = array.length>>1; + array = Arrays.copyOf(array, shrinkSize); + } + /** * {@inheritDoc} */ @@ -210,8 +289,8 @@ public boolean validate() { /** * Validate the node for the heap invariants. * - * @param node - * to validate for. + * @param index + * of node to validate for. * @return True if node is valid. */ private boolean validateNode(int index) { @@ -262,7 +341,7 @@ public T[] getHeap() { */ @Override public T getHeadValue() { - if (array.length == 0) return null; + if (size == 0 || array.length == 0) return null; return array[0]; } @@ -274,61 +353,6 @@ public T removeHead() { return remove(getHeadValue()); } - protected void heapDown(int index) { - T value = this.array[index]; - int leftIndex = getLeftIndex(index); - int rightIndex = getRightIndex(index); - T left = (leftIndex != Integer.MIN_VALUE && leftIndex < this.size) ? this.array[leftIndex] : null; - T right = (rightIndex != Integer.MIN_VALUE && rightIndex < this.size) ? this.array[rightIndex] : null; - - if (left == null && right == null) { - // Nothing to do here - return; - } - - T nodeToMove = null; - int nodeToMoveIndex = -1; - if ((type == Type.MIN && left != null && right != null && value.compareTo(left) > 0 && value.compareTo(right) > 0) - || (type == Type.MAX && left != null && right != null && value.compareTo(left) < 0 && value.compareTo(right) < 0)) { - // Both children are greater/lesser than node - if ((type == Type.MIN && right.compareTo(left) < 0) || (type == Type.MAX && right.compareTo(left) > 0)) { - // Right is greater/lesser than left - nodeToMove = right; - nodeToMoveIndex = rightIndex; - } else if ((type == Type.MIN && left.compareTo(right) < 0) - || (type == Type.MAX && left.compareTo(right) > 0) - ) { - // Left is greater/lesser than right - nodeToMove = left; - nodeToMoveIndex = leftIndex; - } else { - // Both children are equal, use right - nodeToMove = right; - nodeToMoveIndex = rightIndex; - } - } else if ((type == Type.MIN && right != null && value.compareTo(right) > 0) - || (type == Type.MAX && right != null && value.compareTo(right) < 0) - ) { - // Right is greater/lesser than node - nodeToMove = right; - nodeToMoveIndex = rightIndex; - } else if ((type == Type.MIN && left != null && value.compareTo(left) > 0) - || (type == Type.MAX && left != null && value.compareTo(left) < 0) - ) { - // Left is greater/lesser than node - nodeToMove = left; - nodeToMoveIndex = leftIndex; - } - // No node to move, stop recursion - if (nodeToMove == null) return; - - // Re-factor heap sub-tree - this.array[nodeToMoveIndex] = value; - this.array[index] = nodeToMove; - - heapDown(nodeToMoveIndex); - } - /** * {@inheritDoc} */ @@ -348,7 +372,7 @@ public String toString() { protected static class HeapPrinter { public static > String getString(BinaryHeapArray tree) { - if (tree.array.length == 0) + if (tree.size == 0 || tree.array.length == 0) return "Tree has no nodes."; T root = tree.array[0]; @@ -425,11 +449,12 @@ public int size() { /** * Get the navigation directions through the tree to the index. * - * @param index - * of the Node to get directions for. + * @param idx + * index of the Node to get directions for. * @return Integer array representing the directions to the index. */ - private int[] getDirections(int index) { + private static int[] getDirections(int idx) { + int index = idx; int directionsSize = (int) (Math.log10(index + 1) / Math.log10(2)) - 1; int[] directions = null; if (directionsSize > 0) { @@ -583,7 +608,6 @@ private Node getNode(Node startingNode, T value) { } else if (startingNode != null && !startingNode.value.equals(value)) { Node left = startingNode.left; Node right = startingNode.right; - Type type = this.type; if (left != null && ((type==Type.MIN && left.value.compareTo(value)<=0)||(type==Type.MAX && left.value.compareTo(value)>=0)) ) { @@ -637,16 +661,18 @@ public T remove(T value) { /** * Heap up the heap from this node. * - * @param node + * @param nodeToHeapUp * to heap up. */ - protected void heapUp(Node node) { + protected void heapUp(Node nodeToHeapUp) { + Node node = nodeToHeapUp; while (node != null) { Node heapNode = node; Node parent = heapNode.parent; - if ((type == Type.MIN && parent != null && node.value.compareTo(parent.value) < 0) - || (type == Type.MAX && parent != null && node.value.compareTo(parent.value) > 0)) { + if ((parent != null) && + ((type == Type.MIN && node.value.compareTo(parent.value) < 0) || (type == Type.MAX && node.value.compareTo(parent.value) > 0)) + ) { // Node is less than parent, switch node with parent Node grandParent = parent.parent; Node parentLeft = parent.left; @@ -690,12 +716,13 @@ protected void heapUp(Node node) { /** * Heap down the heap from this node. * - * @param node + * @param nodeToHeapDown * to heap down. */ - protected void heapDown(Node node) { - if (node==null) return; + protected void heapDown(Node nodeToHeapDown) { + if (nodeToHeapDown==null) return; + Node node = nodeToHeapDown; Node heapNode = node; Node left = heapNode.left; Node right = heapNode.right; @@ -707,8 +734,10 @@ protected void heapDown(Node node) { Node nodeToMove = null; - if ((type == Type.MIN && left != null && right != null && node.value.compareTo(left.value) > 0 && node.value.compareTo(right.value) > 0) - || (type == Type.MAX && left != null && right != null && node.value.compareTo(left.value) < 0 && node.value.compareTo(right.value) < 0)) { + if ((left != null && right != null ) && + ((type == Type.MIN && node.value.compareTo(left.value) > 0 && node.value.compareTo(right.value) > 0) + || (type == Type.MAX && node.value.compareTo(left.value) < 0 && node.value.compareTo(right.value) < 0)) + ) { // Both children are greater/lesser than node if ((type == Type.MIN && right.value.compareTo(left.value) < 0) || (type == Type.MAX && right.value.compareTo(left.value) > 0)) { // Right is greater/lesser than left @@ -823,12 +852,13 @@ private boolean validateNode(Node node) { * * @param node * to populate. - * @param index + * @param idx * of node in array. * @param array * where the node lives. */ - private void getNodeValue(Node node, int index, T[] array) { + private void getNodeValue(Node node, int idx, T[] array) { + int index = idx; array[index] = node.value; index = (index * 2) + 1; @@ -967,6 +997,7 @@ public JavaCompatibleBinaryHeapArray(BinaryHeapArray heap) { /** * {@inheritDoc} */ + @Override public boolean add(T value) { return heap.add(value); } @@ -974,6 +1005,7 @@ public boolean add(T value) { /** * {@inheritDoc} */ + @Override public boolean remove(Object value) { return (heap.remove((T)value)!=null); } @@ -981,6 +1013,7 @@ public boolean remove(Object value) { /** * {@inheritDoc} */ + @Override public boolean contains(Object value) { return heap.contains((T)value); } @@ -1063,6 +1096,7 @@ public boolean add(T value) { /** * {@inheritDoc} */ + @Override public boolean remove(Object value) { return (heap.remove((T)value)!=null); } @@ -1070,6 +1104,7 @@ public boolean remove(Object value) { /** * {@inheritDoc} */ + @Override public boolean contains(Object value) { return heap.contains((T)value); } diff --git a/src/com/jwetherell/algorithms/data_structures/BinarySearchTree.java b/src/com/jwetherell/algorithms/data_structures/BinarySearchTree.java index 6da25208..f956090c 100644 --- a/src/com/jwetherell/algorithms/data_structures/BinarySearchTree.java +++ b/src/com/jwetherell/algorithms/data_structures/BinarySearchTree.java @@ -1,5 +1,6 @@ package com.jwetherell.algorithms.data_structures; +import java.lang.reflect.Array; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; @@ -9,6 +10,8 @@ import java.util.Queue; import java.util.Set; +import com.jwetherell.algorithms.data_structures.interfaces.ITree; + /** * A binary search tree (BST), which may sometimes also be called an ordered or * sorted binary tree, is a node-based binary tree data structure which has the @@ -16,9 +19,9 @@ * keys less than the node's key. 2) The right subtree of a node contains only * nodes with keys greater than the node's key. 3) Both the left and right * subtrees must also be binary search trees. - * - * http://en.wikipedia.org/wiki/Binary_search_tree - * + *

+ * @see Binary Search Tree (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") @@ -28,22 +31,28 @@ public class BinarySearchTree> implements ITree { protected static final Random RANDOM = new Random(); - protected enum Position { - LEFT, RIGHT - }; - protected Node root = null; protected int size = 0; protected INodeCreator creator = null; public enum DepthFirstSearchOrder { inOrder, preOrder, postOrder - }; + } /** * Default constructor. */ - public BinarySearchTree() { } + public BinarySearchTree() { + this.creator = new INodeCreator() { + /** + * {@inheritDoc} + */ + @Override + public Node createNewNode(Node parent, T id) { + return (new Node(parent, id)); + } + }; + } /** * Constructor with external Node creator. @@ -70,11 +79,7 @@ public boolean add(T value) { * @return Node which was added to the tree. */ protected Node addValue(T value) { - Node newNode = null; - if (this.creator == null) - newNode = new Node(null, value); - else - newNode = this.creator.createNewNode(null, value); + Node newNode = this.creator.createNewNode(null, value); // If root is null, assign if (root == null) { @@ -131,12 +136,12 @@ public boolean contains(T value) { protected Node getNode(T value) { Node node = root; while (node != null && node.id != null) { - if (value.compareTo(node.id) == 0) { - return node; - } else if (value.compareTo(node.id) < 0) { + if (value.compareTo(node.id) < 0) { node = node.lesser; - } else { + } else if (value.compareTo(node.id) > 0) { node = node.greater; + } else if (value.compareTo(node.id) == 0) { + return node; } } return null; @@ -149,39 +154,30 @@ protected Node getNode(T value) { * Root of tree to rotate left. */ protected void rotateLeft(Node node) { - Position parentPosition = null; Node parent = node.parent; - if (parent != null) { - if (node.equals(parent.lesser)) { - // Lesser - parentPosition = Position.LEFT; - } else { - // Greater - parentPosition = Position.RIGHT; - } - } - Node greater = node.greater; - node.greater = null; Node lesser = greater.lesser; greater.lesser = node; node.parent = greater; node.greater = lesser; + if (lesser != null) lesser.parent = node; - if (parentPosition != null) { - if (parentPosition == Position.LEFT) { + if (parent!=null) { + if (node == parent.lesser) { parent.lesser = greater; - } else { + } else if (node == parent.greater) { parent.greater = greater; + } else { + throw new RuntimeException("Yikes! I'm not related to my parent. " + node.toString()); } greater.parent = parent; } else { root = greater; - greater.parent = null; + root.parent = null; } } @@ -189,42 +185,33 @@ protected void rotateLeft(Node node) { * Rotate tree right at sub-tree rooted at node. * * @param node - * Root of tree to rotate left. + * Root of tree to rotate right. */ protected void rotateRight(Node node) { - Position parentPosition = null; Node parent = node.parent; - if (parent != null) { - if (node.equals(parent.lesser)) { - // Lesser - parentPosition = Position.LEFT; - } else { - // Greater - parentPosition = Position.RIGHT; - } - } - Node lesser = node.lesser; - node.lesser = null; Node greater = lesser.greater; lesser.greater = node; node.parent = lesser; node.lesser = greater; + if (greater != null) greater.parent = node; - if (parentPosition != null) { - if (parentPosition == Position.LEFT) { + if (parent!=null) { + if (node == parent.lesser) { parent.lesser = lesser; - } else { + } else if (node == parent.greater) { parent.greater = lesser; + } else { + throw new RuntimeException("Yikes! I'm not related to my parent. " + node.toString()); } lesser.parent = parent; } else { root = lesser; - lesser.parent = null; + root.parent = null; } } @@ -294,7 +281,8 @@ public T remove(T value) { */ protected Node removeValue(T value) { Node nodeToRemoved = this.getNode(value); - if (nodeToRemoved != null) nodeToRemoved = removeNode(nodeToRemoved); + if (nodeToRemoved != null) + nodeToRemoved = removeNode(nodeToRemoved); return nodeToRemoved; } @@ -326,15 +314,8 @@ protected Node removeNode(Node nodeToRemoved) { */ protected Node getReplacementNode(Node nodeToRemoved) { Node replacement = null; - if (nodeToRemoved.lesser != null && nodeToRemoved.greater == null) { - // Using the less subtree - replacement = nodeToRemoved.lesser; - } else if (nodeToRemoved.greater != null && nodeToRemoved.lesser == null) { - // Using the greater subtree (there is no lesser subtree, no - // refactoring) - replacement = nodeToRemoved.greater; - } else if (nodeToRemoved.greater != null && nodeToRemoved.lesser != null) { - // Two children + if (nodeToRemoved.greater != null && nodeToRemoved.lesser != null) { + // Two children. // Add some randomness to deletions, so we don't always use the // greatest/least on deletion if (modifications % 2 != 0) { @@ -347,6 +328,12 @@ protected Node getReplacementNode(Node nodeToRemoved) { replacement = nodeToRemoved.greater; } modifications++; + } else if (nodeToRemoved.lesser != null && nodeToRemoved.greater == null) { + // Using the less subtree + replacement = nodeToRemoved.lesser; + } else if (nodeToRemoved.greater != null && nodeToRemoved.lesser == null) { + // Using the greater subtree (there is no lesser subtree, no refactoring) + replacement = nodeToRemoved.greater; } return replacement; } @@ -369,26 +356,26 @@ protected void replaceNodeWithNode(Node nodeToRemoved, Node replacementNod // Replace replacementNode's branches with nodeToRemove's branches Node nodeToRemoveLesser = nodeToRemoved.lesser; - if (nodeToRemoveLesser != null && !nodeToRemoveLesser.equals(replacementNode)) { + if (nodeToRemoveLesser != null && nodeToRemoveLesser != replacementNode) { replacementNode.lesser = nodeToRemoveLesser; nodeToRemoveLesser.parent = replacementNode; } Node nodeToRemoveGreater = nodeToRemoved.greater; - if (nodeToRemoveGreater != null && !nodeToRemoveGreater.equals(replacementNode)) { + if (nodeToRemoveGreater != null && nodeToRemoveGreater != replacementNode) { replacementNode.greater = nodeToRemoveGreater; nodeToRemoveGreater.parent = replacementNode; } // Remove link from replacementNode's parent to replacement Node replacementParent = replacementNode.parent; - if (replacementParent != null && !replacementParent.equals(nodeToRemoved)) { + if (replacementParent != null && replacementParent != nodeToRemoved) { Node replacementParentLesser = replacementParent.lesser; Node replacementParentGreater = replacementParent.greater; - if (replacementParentLesser != null && replacementParentLesser.equals(replacementNode)) { + if (replacementParentLesser != null && replacementParentLesser == replacementNode) { replacementParent.lesser = replacementNodeGreater; if (replacementNodeGreater != null) replacementNodeGreater.parent = replacementParent; - } else if (replacementParentGreater != null && replacementParentGreater.equals(replacementNode)) { + } else if (replacementParentGreater != null && replacementParentGreater == replacementNode) { replacementParent.greater = replacementNodeLesser; if (replacementNodeLesser != null) replacementNodeLesser.parent = replacementParent; @@ -476,11 +463,23 @@ protected boolean validateNode(Node node) { * * @return breath first search sorted array representing the tree. */ - public T[] getBFS() { - Queue> queue = new ArrayDeque>(); - T[] values = (T[]) new Comparable[size]; + public T[] getBFS() { + return getBFS(this.root, this.size); + } + + /** + * Get an array representation of the tree in breath first search order. + * + * @param start rooted node + * @param size of tree rooted at start + * + * @return breath first search sorted array representing the tree. + */ + public static > T[] getBFS(Node start, int size) { + final Queue> queue = new ArrayDeque>(); + final T[] values = (T[])Array.newInstance(start.id.getClass(), size); int count = 0; - Node node = root; + Node node = start; while (node != null) { values[count++] = node.id; if (node.lesser != null) @@ -507,13 +506,28 @@ public T[] getLevelOrder() { /** * Get an array representation of the tree in-order. * - * @return in-order sorted array representing the tree. + * @param order of search + * + * @return order sorted array representing the tree. */ public T[] getDFS(DepthFirstSearchOrder order) { - Set> added = new HashSet>(2); - T[] nodes = (T[]) new Comparable[size]; + return getDFS(order, this.root, this.size); + } + + /** + * Get an array representation of the tree in-order. + * + * @param order of search + * @param start rooted node + * @param size of tree rooted at start + * + * @return order sorted array representing the tree. + */ + public static > T[] getDFS(DepthFirstSearchOrder order, Node start, int size) { + final Set> added = new HashSet>(2); + final T[] nodes = (T[])Array.newInstance(start.id.getClass(), size); int index = 0; - Node node = root; + Node node = start; while (index < size && node != null) { Node parent = node.parent; Node lesser = (node.lesser != null && !added.contains(node.lesser)) ? node.lesser : null; @@ -562,17 +576,13 @@ public T[] getDFS(DepthFirstSearchOrder order) { if (lesser != null) { node = lesser; } else { - if (!added.contains(node)) { - nodes[index++] = node.id; - added.add(node); - } if (greater != null) { node = greater; - } else if (added.contains(node)) { - node = parent; } else { - // We should not get here. Stop the loop! - node = null; + // lesser==null && greater==null + nodes[index++] = node.id; + added.add(node); + node = parent; } } } @@ -631,7 +641,7 @@ protected Node(Node parent, T id) { */ @Override public String toString() { - return "id =" + id + " parent=" + ((parent != null) ? parent.id : "NULL") + " lesser=" + return "id=" + id + " parent=" + ((parent != null) ? parent.id : "NULL") + " lesser=" + ((lesser != null) ? lesser.id : "NULL") + " greater=" + ((greater != null) ? greater.id : "NULL"); } } @@ -682,8 +692,7 @@ private static > String getString(Node node, String p builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() >= 1) { - builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), - true)); + builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } diff --git a/src/com/jwetherell/algorithms/data_structures/CompactSuffixTrie.java b/src/com/jwetherell/algorithms/data_structures/CompactSuffixTrie.java index 0539870c..b2796543 100644 --- a/src/com/jwetherell/algorithms/data_structures/CompactSuffixTrie.java +++ b/src/com/jwetherell/algorithms/data_structures/CompactSuffixTrie.java @@ -8,11 +8,12 @@ * string in a way that allows for a particularly fast implementation of many * important string operations. This implementation is based upon a patricia * trie which IS a compact trie. - * - * http://en.wikipedia.org/wiki/Suffix_trie - * + *

+ * @see Suffix Trie (Wikipedia) + *
* @author Justin Wetherell */ +@SuppressWarnings("unchecked") public class CompactSuffixTrie { private PatriciaTrie tree = null; @@ -23,7 +24,6 @@ public class CompactSuffixTrie { * @param sequence * to create a suffix trie from. */ - @SuppressWarnings("unchecked") public CompactSuffixTrie(C sequence) { tree = new PatriciaTrie(); int length = sequence.length(); @@ -40,7 +40,6 @@ public CompactSuffixTrie(C sequence) { * to add to trie. * @return True if added successfully. */ - @SuppressWarnings("unchecked") public boolean add(C sequence) { int length = sequence.length(); for (int i = 0; i < length; i++) { @@ -142,4 +141,9 @@ private Set getSuffixes(PatriciaTrie.Node node, String prefix) { public String toString() { return PatriciaTrie.PatriciaTriePrinter.getString(tree); } + + public boolean equals(CompactSuffixTrie trie){ + if(this.getSuffixes().equals(trie.getSuffixes())) return true; + return false; + } } diff --git a/src/com/jwetherell/algorithms/data_structures/DisjointSet.java b/src/com/jwetherell/algorithms/data_structures/DisjointSet.java new file mode 100644 index 00000000..c7c02a08 --- /dev/null +++ b/src/com/jwetherell/algorithms/data_structures/DisjointSet.java @@ -0,0 +1,138 @@ +package com.jwetherell.algorithms.data_structures; + +/** + * In computer science, a disjoint-set data structure, also called a union–find data structure or merge–find set, is a data structure that keeps track of a set of + * elements partitioned into a number of disjoint (non-overlapping) subsets. + *

+ * It supports two useful operations:
+ * Find: Determine which subset a particular element is in. Find typically returns an item from this set that serves as its "representative"; by comparing the + * result of two Find operations, one can determine whether two elements are in the same subset.
+ * Union: Join two subsets into a single subset.
+ *

+ * @see Disjoint Set (Wikipedia) + *
+ * @author Justin Wetherell + */ +@SuppressWarnings("unchecked") +public class DisjointSet { + + private DisjointSet() { } + + /** + * Creates a set of one element. + * + * @param v Value to use when creating the set + * @return Item representing the value + */ + public static final Item makeSet(T v) { + final Item item = new Item(null,v); + item.parent = item; + return item; + } + + /** + * Determine which subset a particular element is in. Find returns an item from this set that serves as its "representative"; by comparing the result + * of two Find operations, one can determine whether two elements are in the same subset. This method uses path compression which is a way of flattening + * the structure of the tree whenever Find is used on it. + * + * @param x Find the "representative" of this Item + * @return "Representative" of this Item + */ + public static final Item find(Item x) { + if (x == null) + return null; + + if ((x.parent!=null) && !(x.parent.equals(x))) + return x.parent = find(x.parent); + return x.parent; + } + + /** + * Join two subsets into a single subset. This method uses 'union by rank' which always attaches the smaller tree to the root of the larger tree. + * + * @param x Subset 1 to join + * @param y Subset 2 to join + * @return Resulting Set of joining Subset 1 and Subset 2 + */ + public static final Item union(Item x, Item y) { + final Item xRoot = find(x); + final Item yRoot = find(y); + if (xRoot==null && yRoot==null) + return null; + if (xRoot==null && yRoot!=null) + return yRoot; + if (yRoot==null && xRoot!=null) + return xRoot; + + // x and y are in the same set + if (xRoot.equals(yRoot)) + return xRoot; + + if (xRoot.rank < yRoot.rank) { + xRoot.parent = yRoot; + return yRoot; + } else if (xRoot.rank > yRoot.rank) { + yRoot.parent = xRoot; + return xRoot; + } + // else + yRoot.parent = xRoot; + xRoot.rank = xRoot.rank + 1; + return xRoot; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Nothing here to see, yet."; + } + + public static final class Item { + + private Item parent; + private T value; + /** Rank is not the actual depth of the tree rather it is an upper bound. As such, on a find operation, the rank is allowed to get out of sync with the depth. **/ + private long rank; + + public Item(Item parent, T value) { + this.parent = parent; + this.value = value; + this.rank = 0; + } + + public T getValue() { + return value; + } + public long getRank() { + return rank; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Item)) + return false; + + final Item i = (Item) o; + if ((i.parent!=null && parent!=null) && !(i.parent.value.equals(parent.value))) + return false; + if ((i.value!=null && value!=null) && !(i.value.equals(value))) + return false; + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "parent="+(parent!=null?parent.value:null)+" "+ + (rank>0?"rank="+rank +" " : "") + + "value="+(value!=null?value:null); + } + } +} diff --git a/src/com/jwetherell/algorithms/data_structures/FenwickTree.java b/src/com/jwetherell/algorithms/data_structures/FenwickTree.java new file mode 100644 index 00000000..60634d40 --- /dev/null +++ b/src/com/jwetherell/algorithms/data_structures/FenwickTree.java @@ -0,0 +1,422 @@ +package com.jwetherell.algorithms.data_structures; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +/** + * A Fenwick tree or binary indexed tree is a data structure providing efficient methods + * for calculation and manipulation of the prefix sums of a table of values. Fenwick trees + * primarily solve the problem of balancing prefix sum calculation efficiency with element + * modification efficiency. + *

+ * This class is meant to be somewhat generic, all you'd have to do is extend + * the Data abstract class to store your custom data. I've included a range sum + * implementations. + *

+ * @see Fenwick Tree (Wikipedia) + *
+ * @author Justin Wetherell + */ +@SuppressWarnings("unchecked") +public class FenwickTree { + + private Object[] array; + + public FenwickTree(List data) { + // Find the largest index + int n = 0; + for (Data d : data) + if (d.index > n) + n = d.index; + n = next(n+1); + array = new Object[n]; + + // Add the data + for (D d : data) + update(d.index, d); + } + + /** + * Stabbing query + * + * @param index + * index for query + * @return data at index. + */ + public D query(int index) { + return query(index, index); + } + + /** + * Range query + * + * @param start + * start of range (inclusive) + * @param end + * end of range (inclusive) + * @return data for range. + */ + public D query(int start, int end) { + final D e = lookup(end); + final D s = lookup(start-1); + final D c = (D) e.copy(); + if (s != null) + c.separate(s); + return c; + } + + private D lookup(int index) { + index++; // tree index is 1 based + index = Math.min(array.length - 1, index); + if (index <= 0) + return null; + + D res = null; + while (index > 0) { + if (res == null) { + final D data = (D) array[index]; + if (data != null) + res = (D) data.copy(); + } else{ + res.combined((D) array[index]); + } + index = prev(index); + } + return res; + } + + private void update(int index, D value) { + index++; // tree index is 1 based + while (index < array.length) { + D data = (D) array[index]; + if (data == null) { + data = (D) value.copy(); + data.index = index; + array[index] = data; + } else { + data.combined(value); + } + index = next(index); + } + } + + private static final int prev(int x) { + return x & (x - 1); + } + + private static final int next(int x) { + return 2 * x - prev(x); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(FenwickTreePrinter.getString(this)); + return builder.toString(); + } + + protected static class FenwickTreePrinter { + + public static String getString(FenwickTree tree) { + if (tree.array.length == 0) + return "Tree has no nodes."; + + final D first = (D) tree.array[1]; + if (first == null) + return "Tree has no nodes."; + + final StringBuilder builder = new StringBuilder(); + builder.append("└── dummy \n"); + builder.append(getString(tree, 0, tree.array.length, "", true)); + return builder.toString(); + } + + private static String getString(FenwickTree tree, int start, int end, String prefix, boolean isTail) { + if (end > tree.array.length || (end - start == 0)) + return ""; + + final StringBuilder builder = new StringBuilder(); + + final D value = (D) tree.array[start]; + if (value != null) + builder.append(prefix + (isTail ? "└── " : "├── ") + value + "\n"); + + int next = start + 1; + final List children = new ArrayList(2); + while (next < end) { + children.add(next); + next = next(next); + } + for (int i = 0; i < children.size() - 1; i++) + builder.append(getString(tree, children.get(i), children.get(i+1), prefix + (isTail ? " " : "│ "), false)); + if (children.size() >= 1) + builder.append(getString(tree, children.get(children.size()-1), end, prefix + (isTail ? " " : "│ "), true)); + + return builder.toString(); + } + } + + public abstract static class Data implements Comparable { + + protected int index = Integer.MIN_VALUE; + + /** + * Constructor for data at index. + * + * @param index + * of data. + */ + protected Data(int index) { + this.index = index; + } + + /** + * Clear the indices. + */ + public void clear() { + index = Integer.MIN_VALUE; + } + + /** + * Combined this data with the Data parameter. + * + * @param data + * to combined with. + * @return Data which represents the combination. + */ + public abstract Data combined(Data data); + + /** + * Separate this data with the Data parameter. + * + * @param data + * to separate with. + * @return Data which represents the combination. + */ + public abstract Data separate(Data data); + + /** + * Deep copy of data. + * + * @return deep copy. + */ + public abstract Data copy(); + + /** + * Query inside this data object. + * + * @param startOfRange + * of range to query for. + * @param endOfRange + * of range to query for. + * @return Data queried for or NULL if it doesn't match the query. + */ + public abstract Data query(long startOfRange, long endOfRange); + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("[").append(index).append("]"); + return builder.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public int compareTo(Data d) { + if (this.index < d.index) + return -1; + if (d.index < this.index) + return 1; + return 0; + } + + /** + * Data structure representing sum of the range. + */ + public static final class RangeSumData extends Data { + + public N sum = null; + + public RangeSumData(int index, N number) { + super(index); + + this.sum = number; + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() { + super.clear(); + + sum = null; + } + + /** + * {@inheritDoc} + */ + @Override + public Data combined(Data data) { + RangeSumData q = null; + if (data instanceof RangeSumData) { + q = (RangeSumData) data; + this.combined(q); + } + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public Data separate(Data data) { + RangeSumData q = null; + if (data instanceof RangeSumData) { + q = (RangeSumData) data; + this.separate(q); + } + return this; + } + + /** + * Combined range sum data. + * + * @param data + * resulted from combination. + */ + private void combined(RangeSumData data) { + if (this.sum == null && data.sum == null) + return; + else if (this.sum != null && data.sum == null) + return; + else if (this.sum == null && data.sum != null) + this.sum = data.sum; + else { + /* TODO: This is ugly and how to handle number overflow? */ + if (this.sum instanceof BigDecimal || data.sum instanceof BigDecimal) { + BigDecimal result = ((BigDecimal)this.sum).add((BigDecimal)data.sum); + this.sum = (N)result; + } else if (this.sum instanceof BigInteger || data.sum instanceof BigInteger) { + BigInteger result = ((BigInteger)this.sum).add((BigInteger)data.sum); + this.sum = (N)result; + } else if (this.sum instanceof Long || data.sum instanceof Long) { + Long result = (this.sum.longValue() + data.sum.longValue()); + this.sum = (N)result; + } else if (this.sum instanceof Double || data.sum instanceof Double) { + Double result = (this.sum.doubleValue() + data.sum.doubleValue()); + this.sum = (N)result; + } else if (this.sum instanceof Float || data.sum instanceof Float) { + Float result = (this.sum.floatValue() + data.sum.floatValue()); + this.sum = (N)result; + } else { + // Integer + Integer result = (this.sum.intValue() + data.sum.intValue()); + this.sum = (N)result; + } + } + } + + /** + * Separate range sum data. + * + * @param data + * resulted from combination. + */ + private void separate(RangeSumData data) { + if (this.sum == null && data.sum == null) + return; + else if (this.sum != null && data.sum == null) + return; + else if (this.sum == null && data.sum != null) + this.sum = data.sum; + else { + /* TODO: This is ugly and how to handle number overflow? */ + if (this.sum instanceof BigDecimal || data.sum instanceof BigDecimal) { + BigDecimal result = ((BigDecimal)this.sum).subtract((BigDecimal)data.sum); + this.sum = (N)result; + } else if (this.sum instanceof BigInteger || data.sum instanceof BigInteger) { + BigInteger result = ((BigInteger)this.sum).subtract((BigInteger)data.sum); + this.sum = (N)result; + } else if (this.sum instanceof Long || data.sum instanceof Long) { + Long result = (this.sum.longValue() - data.sum.longValue()); + this.sum = (N)result; + } else if (this.sum instanceof Double || data.sum instanceof Double) { + Double result = (this.sum.doubleValue() - data.sum.doubleValue()); + this.sum = (N)result; + } else if (this.sum instanceof Float || data.sum instanceof Float) { + Float result = (this.sum.floatValue() - data.sum.floatValue()); + this.sum = (N)result; + } else { + // Integer + Integer result = (this.sum.intValue() - data.sum.intValue()); + this.sum = (N)result; + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public Data copy() { + return new RangeSumData(index, sum); + } + + /** + * {@inheritDoc} + */ + @Override + public Data query(long startOfQuery, long endOfQuery) { + if (endOfQuery < this.index || startOfQuery > this.index) + return null; + + return copy(); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return 31 * (int)(this.index + this.sum.hashCode()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RangeSumData)) + return false; + + RangeSumData data = (RangeSumData) obj; + if (this.index == data.index && this.sum.equals(data.sum)) + return true; + + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(super.toString()).append(" "); + builder.append("sum=").append(sum); + return builder.toString(); + } + } + } +} diff --git a/src/com/jwetherell/algorithms/data_structures/Graph.java b/src/com/jwetherell/algorithms/data_structures/Graph.java index 35100660..c28bbc6e 100644 --- a/src/com/jwetherell/algorithms/data_structures/Graph.java +++ b/src/com/jwetherell/algorithms/data_structures/Graph.java @@ -1,87 +1,94 @@ package com.jwetherell.algorithms.data_structures; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; /** * Graph. Could be directed or undirected depending on the TYPE enum. A graph is * an abstract representation of a set of objects where some pairs of the * objects are connected by links. - * - * http://en.wikipedia.org/wiki/Graph_(mathematics) - * + *

+ * @see Graph (Wikipedia) + *
* @author Justin Wetherell */ +@SuppressWarnings("unchecked") public class Graph> { - private List> verticies = new CopyOnWriteArrayList>(); - private List> edges = new CopyOnWriteArrayList>(); + private List> allVertices = new ArrayList>(); + private List> allEdges = new ArrayList>(); public enum TYPE { DIRECTED, UNDIRECTED - }; + } + /** Defaulted to undirected */ private TYPE type = TYPE.UNDIRECTED; - public Graph() { + public Graph() { } + + public Graph(TYPE type) { + this.type = type; } + /** Deep copies **/ public Graph(Graph g) { - // Deep copies + type = g.getType(); - // Copy the vertices (which copies the edges) - for (Vertex v : g.getVerticies()) { - this.verticies.add(new Vertex(v)); - } + // Copy the vertices which also copies the edges + for (Vertex v : g.getVertices()) + this.allVertices.add(new Vertex(v)); - // Update the object references - for (Vertex v : this.verticies) { + for (Vertex v : this.getVertices()) { for (Edge e : v.getEdges()) { - Vertex fromVertex = e.getFromVertex(); - Vertex toVertex = e.getToVertex(); - int indexOfFrom = this.verticies.indexOf(fromVertex); - e.from = this.verticies.get(indexOfFrom); - int indexOfTo = this.verticies.indexOf(toVertex); - e.to = this.verticies.get(indexOfTo); - this.edges.add(e); + this.allEdges.add(e); } } - - type = g.getType(); } - public Graph(TYPE type) { - this(); - this.type = type; - } - - public Graph(List> verticies, List> edges) { - this(TYPE.UNDIRECTED, verticies, edges); + /** + * Creates a Graph from the vertices and edges. This defaults to an undirected Graph + * + * NOTE: Duplicate vertices and edges ARE allowed. + * NOTE: Copies the vertex and edge objects but does NOT store the Collection parameters itself. + * + * @param vertices Collection of vertices + * @param edges Collection of edges + */ + public Graph(Collection> vertices, Collection> edges) { + this(TYPE.UNDIRECTED, vertices, edges); } - public Graph(TYPE type, List> verticies, List> edges) { + /** + * Creates a Graph from the vertices and edges. + * + * NOTE: Duplicate vertices and edges ARE allowed. + * NOTE: Copies the vertex and edge objects but does NOT store the Collection parameters itself. + * + * @param vertices Collection of vertices + * @param edges Collection of edges + */ + public Graph(TYPE type, Collection> vertices, Collection> edges) { this(type); - this.verticies.addAll(verticies); - this.edges.addAll(edges); + + this.allVertices.addAll(vertices); + this.allEdges.addAll(edges); for (Edge e : edges) { - Vertex from = e.from; - Vertex to = e.to; + final Vertex from = e.from; + final Vertex to = e.to; - if (!this.verticies.contains(from) || !this.verticies.contains(to)) + if (!this.allVertices.contains(from) || !this.allVertices.contains(to)) continue; - int index = this.verticies.indexOf(from); - Vertex fromVertex = this.verticies.get(index); - index = this.verticies.indexOf(to); - Vertex toVertex = this.verticies.get(index); - fromVertex.addEdge(e); + from.addEdge(e); if (this.type == TYPE.UNDIRECTED) { - Edge reciprical = new Edge(e.cost, toVertex, fromVertex); - toVertex.addEdge(reciprical); - this.edges.add(reciprical); + Edge reciprical = new Edge(e.cost, to, from); + to.addEdge(reciprical); + this.allEdges.add(reciprical); } } } @@ -90,12 +97,74 @@ public TYPE getType() { return type; } - public List> getVerticies() { - return verticies; + public List> getVertices() { + return allVertices; } public List> getEdges() { - return edges; + return allEdges; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int code = this.type.hashCode() + this.allVertices.size() + this.allEdges.size(); + for (Vertex v : allVertices) + code *= v.hashCode(); + for (Edge e : allEdges) + code *= e.hashCode(); + return 31 * code; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object g1) { + if (!(g1 instanceof Graph)) + return false; + + final Graph g = (Graph) g1; + + final boolean typeEquals = this.type == g.type; + if (!typeEquals) + return false; + + final boolean verticesSizeEquals = this.allVertices.size() == g.allVertices.size(); + if (!verticesSizeEquals) + return false; + + final boolean edgesSizeEquals = this.allEdges.size() == g.allEdges.size(); + if (!edgesSizeEquals) + return false; + + // Vertices can contain duplicates and appear in different order but both arrays should contain the same elements + final Object[] ov1 = this.allVertices.toArray(); + Arrays.sort(ov1); + final Object[] ov2 = g.allVertices.toArray(); + Arrays.sort(ov2); + for (int i=0; i v1 = (Vertex) ov1[i]; + final Vertex v2 = (Vertex) ov2[i]; + if (!v1.equals(v2)) + return false; + } + + // Edges can contain duplicates and appear in different order but both arrays should contain the same elements + final Object[] oe1 = this.allEdges.toArray(); + Arrays.sort(oe1); + final Object[] oe2 = g.allEdges.toArray(); + Arrays.sort(oe2); + for (int i=0; i e1 = (Edge) oe1[i]; + final Edge e2 = (Edge) oe2[i]; + if (!e1.equals(e2)) + return false; + } + + return true; } /** @@ -103,10 +172,9 @@ public List> getEdges() { */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); - for (Vertex v : verticies) { + final StringBuilder builder = new StringBuilder(); + for (Vertex v : allVertices) builder.append(v.toString()); - } return builder.toString(); } @@ -125,12 +193,11 @@ public Vertex(T value, int weight) { this.weight = weight; } + /** Deep copies the edges along with the value and weight **/ public Vertex(Vertex vertex) { this(vertex.value, vertex.weight); - this.edges = new ArrayList>(); - for (Edge e : vertex.edges) { - this.edges.add(new Edge(e)); - } + + this.edges.addAll(vertex.edges); } public T getValue() { @@ -153,6 +220,14 @@ public List> getEdges() { return edges; } + public Edge getEdge(Vertex v) { + for (Edge e : edges) { + if (e.to.equals(v)) + return e; + } + return null; + } + public boolean pathTo(Vertex v) { for (Edge e : edges) { if (e.to.equals(v)) @@ -165,44 +240,89 @@ public boolean pathTo(Vertex v) { * {@inheritDoc} */ @Override - public int compareTo(Vertex v) { - if (this.value == null || v.value == null) - return -1; - return this.value.compareTo(v.value); + public int hashCode() { + final int code = this.value.hashCode() + this.weight + this.edges.size(); + return 31 * code; } /** * {@inheritDoc} */ - @SuppressWarnings("unchecked") @Override public boolean equals(Object v1) { if (!(v1 instanceof Vertex)) return false; - Vertex v = (Vertex) v1; + final Vertex v = (Vertex) v1; + + final boolean weightEquals = this.weight == v.weight; + if (!weightEquals) + return false; - boolean values = this.value.equals(v.value); - if (!values) + final boolean edgesSizeEquals = this.edges.size() == v.edges.size(); + if (!edgesSizeEquals) return false; - boolean weight = this.weight == v.weight; - if (!weight) + final boolean valuesEquals = this.value.equals(v.value); + if (!valuesEquals) return false; + final Iterator> iter1 = this.edges.iterator(); + final Iterator> iter2 = v.edges.iterator(); + while (iter1.hasNext() && iter2.hasNext()) { + // Only checking the cost + final Edge e1 = iter1.next(); + final Edge e2 = iter2.next(); + if (e1.cost != e2.cost) + return false; + } + return true; } + /** + * {@inheritDoc} + */ + @Override + public int compareTo(Vertex v) { + final int valueComp = this.value.compareTo(v.value); + if (valueComp != 0) + return valueComp; + + if (this.weight < v.weight) + return -1; + if (this.weight > v.weight) + return 1; + + if (this.edges.size() < v.edges.size()) + return -1; + if (this.edges.size() > v.edges.size()) + return 1; + + final Iterator> iter1 = this.edges.iterator(); + final Iterator> iter2 = v.edges.iterator(); + while (iter1.hasNext() && iter2.hasNext()) { + // Only checking the cost + final Edge e1 = iter1.next(); + final Edge e2 = iter2.next(); + if (e1.cost < e2.cost) + return -1; + if (e1.cost > e2.cost) + return 1; + } + + return 0; + } + /** * {@inheritDoc} */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("vertex:").append(" value=").append(value).append(" weight=").append(weight).append("\n"); - for (Edge e : edges) { + final StringBuilder builder = new StringBuilder(); + builder.append("Value=").append(value).append(" weight=").append(weight).append("\n"); + for (Edge e : edges) builder.append("\t").append(e.toString()); - } return builder.toString(); } } @@ -215,7 +335,8 @@ public static class Edge> implements Comparable> public Edge(int cost, Vertex from, Vertex to) { if (from == null || to == null) - throw (new NullPointerException("Both 'to' and 'from' Verticies need to be non-NULL.")); + throw (new NullPointerException("Both 'to' and 'from' vertices need to be non-NULL.")); + this.cost = cost; this.from = from; this.to = to; @@ -231,7 +352,6 @@ public int getCost() { public void setCost(int cost) { this.cost = cost; - ; } public Vertex getFromVertex() { @@ -242,60 +362,69 @@ public Vertex getToVertex() { return to; } - /** - * {@inheritDoc} - */ - @Override - public int compareTo(Edge e) { - if (this.cost < e.cost) - return -1; - if (this.cost > e.cost) - return 1; - return 0; - } - /** * {@inheritDoc} */ @Override public int hashCode() { - return this.cost * (this.getFromVertex().value.hashCode() * this.getToVertex().value.hashCode()); + final int cost = (this.cost * (this.getFromVertex().hashCode() * this.getToVertex().hashCode())); + return 31 * cost; } /** * {@inheritDoc} */ - @SuppressWarnings("unchecked") @Override public boolean equals(Object e1) { if (!(e1 instanceof Edge)) return false; - Edge e = (Edge) e1; + final Edge e = (Edge) e1; - boolean costs = this.cost == e.cost; + final boolean costs = this.cost == e.cost; if (!costs) return false; - boolean froms = this.from.equals(e.from); - if (!froms) + final boolean from = this.from.equals(e.from); + if (!from) return false; - boolean tos = this.to.equals(e.to); - if (!tos) + final boolean to = this.to.equals(e.to); + if (!to) return false; return true; } + /** + * {@inheritDoc} + */ + @Override + public int compareTo(Edge e) { + if (this.cost < e.cost) + return -1; + if (this.cost > e.cost) + return 1; + + final int from = this.from.compareTo(e.from); + if (from != 0) + return from; + + final int to = this.to.compareTo(e.to); + if (to != 0) + return to; + + return 0; + } + /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("edge:").append(" [").append(from.value).append("]").append(" -> ").append("[") - .append(to.value).append("]").append(" = ").append(cost).append("\n"); + builder.append("[ ").append(from.value).append("(").append(from.weight).append(") ").append("]").append(" -> ") + .append("[ ").append(to.value).append("(").append(to.weight).append(") ").append("]").append(" = ").append(cost).append("\n"); return builder.toString(); } } @@ -325,6 +454,32 @@ public Vertex getVertex() { return vertex; } + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return 31 * (this.cost * ((this.vertex!=null)?this.vertex.hashCode():1)); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object e1) { + if (!(e1 instanceof CostVertexPair)) + return false; + + final CostVertexPair pair = (CostVertexPair)e1; + if (this.cost != pair.cost) + return false; + + if (!this.vertex.equals(pair.vertex)) + return false; + + return true; + } + /** * {@inheritDoc} */ @@ -332,6 +487,7 @@ public Vertex getVertex() { public int compareTo(CostVertexPair p) { if (p == null) throw new NullPointerException("CostVertexPair 'p' must be non-NULL."); + if (this.cost < p.cost) return -1; if (this.cost > p.cost) @@ -344,8 +500,8 @@ public int compareTo(CostVertexPair p) { */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Vertex=").append(vertex.getValue()).append(" cost=").append(cost).append("\n"); + final StringBuilder builder = new StringBuilder(); + builder.append(vertex.getValue()).append(" (").append(vertex.weight).append(") ").append(" cost=").append(cost).append("\n"); return builder.toString(); } } @@ -353,9 +509,9 @@ public String toString() { public static class CostPathPair> { private int cost = 0; - private Set> path = null; + private List> path = null; - public CostPathPair(int cost, Set> path) { + public CostPathPair(int cost, List> path) { if (path == null) throw (new NullPointerException("path cannot be NULL.")); @@ -371,21 +527,55 @@ public void setCost(int cost) { this.cost = cost; } - public Set> getPath() { + public List> getPath() { return path; } + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int hash = this.cost; + for (Edge e : path) + hash *= e.cost; + return 31 * hash; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CostPathPair)) + return false; + + final CostPathPair pair = (CostPathPair) obj; + if (this.cost != pair.cost) + return false; + + final Iterator iter1 = this.getPath().iterator(); + final Iterator iter2 = pair.getPath().iterator(); + while (iter1.hasNext() && iter2.hasNext()) { + Edge e1 = (Edge) iter1.next(); + Edge e2 = (Edge) iter2.next(); + if (!e1.equals(e2)) + return false; + } + + return true; + } + /** * {@inheritDoc} */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append("Cost = ").append(cost).append("\n"); - for (Edge e : path) { + for (Edge e : path) builder.append("\t").append(e); - } return builder.toString(); } } -} +} \ No newline at end of file diff --git a/src/com/jwetherell/algorithms/data_structures/HashArrayMappedTrie.java b/src/com/jwetherell/algorithms/data_structures/HashArrayMappedTrie.java index 56b1f437..b33d361b 100644 --- a/src/com/jwetherell/algorithms/data_structures/HashArrayMappedTrie.java +++ b/src/com/jwetherell/algorithms/data_structures/HashArrayMappedTrie.java @@ -4,16 +4,18 @@ import java.util.List; import java.util.Map; +import com.jwetherell.algorithms.data_structures.interfaces.IMap; + /** * A hash array mapped trie (HAMT) is an implementation of an associative * array that combines the characteristics of a hash table and an array mapped * trie. It is a refined version of the more general notion of a hash tree. - * + *

* This implementation is 32-bit and steps in 5-bit intervals, maximum tree * height of 7. - * - * http://en.wikipedia.org/wiki/Hash_array_mapped_trie - * + *

+ * @see Hash Array Mapped Trie (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") @@ -27,21 +29,6 @@ public class HashArrayMappedTrie implements IMap { private Node root = null; private int size = 0; - /** - * Convert a big-endian binary string to a little-endian binary string - * and also pads with zeros to make it "BITS" length. - * - * e.g. BITS=5 value==6 big-endian=110 little-endian=011 (pad) return=01100 - */ - private static final String toBinaryString(int value) { - StringBuilder builder = new StringBuilder(Integer.toBinaryString(value)); - builder = builder.reverse(); - while (builder.length() kvNode = (KeyValueNode) node; if (key==kvNode.key) { @@ -59,13 +47,15 @@ private V put(ArrayNode parent, Node node, byte height, int key, V value) { kvNode.value = value; return value; } + // Key already exists but doesn't match current key KeyValueNode oldParent = kvNode; - int newParentPosition = getPosition(height-1, key); - int oldParentPosition = getPosition(height, oldParent.key); - int childPosition = getPosition(height, key); - ArrayNode newParent = new ArrayNode(parent, key, height); + int newParentPosition = getPosition(newHeight-1, key); + int oldParentPosition = getPosition(newHeight, oldParent.key); + int childPosition = getPosition(newHeight, key); + ArrayNode newParent = new ArrayNode(parent, key, newHeight); newParent.parent = parent; + if (parent==null) { // Only the root doesn't have a parent, so new root root = newParent; @@ -73,6 +63,7 @@ private V put(ArrayNode parent, Node node, byte height, int key, V value) { // Add the child to the parent in it's parent's position parent.addChild(newParentPosition, newParent); } + if (oldParentPosition != childPosition) { // The easy case, the two children have different positions in parent newParent.addChild(oldParentPosition, oldParent); @@ -80,20 +71,20 @@ private V put(ArrayNode parent, Node node, byte height, int key, V value) { newParent.addChild(childPosition, new KeyValueNode(newParent, key, value)); return null; } + while (oldParentPosition == childPosition) { // Handle the case when the new children map to same position. - height++; - if (height>MAX_DEPTH) { - // We have found two keys which match exactly. - System.err.println("Yikes! Found two keys which match exactly."); - return null; + newHeight++; + if (newHeight>MAX_DEPTH) { + // We have found two keys which match exactly. I really don't know what to do. + throw new RuntimeException("Yikes! Found two keys which match exactly."); } - newParentPosition = getPosition(height-1, key); - ArrayNode newParent2 = new ArrayNode(newParent, key, height); + newParentPosition = getPosition(newHeight-1, key); + ArrayNode newParent2 = new ArrayNode(newParent, key, newHeight); newParent.addChild(newParentPosition, newParent2); - oldParentPosition = getPosition(height, oldParent.key); - childPosition = getPosition(height, key); + oldParentPosition = getPosition(newHeight, oldParent.key); + childPosition = getPosition(newHeight, key); if (oldParentPosition != childPosition) { newParent2.addChild(oldParentPosition, oldParent); oldParent.parent = newParent2; @@ -107,12 +98,14 @@ private V put(ArrayNode parent, Node node, byte height, int key, V value) { ArrayNode arrayRoot = (ArrayNode) node; int position = getPosition(arrayRoot.height, key); Node child = arrayRoot.getChild(position); + if (child==null) { // Found an empty slot in parent arrayRoot.addChild(position, new KeyValueNode(arrayRoot, key, value)); return null; } - return put(arrayRoot, child, (byte)(height+1), key, value); + + return put(arrayRoot, child, (byte)(newHeight+1), key, value); } return null; } @@ -124,11 +117,10 @@ private V put(ArrayNode parent, Node node, byte height, int key, V value) { public V put(K key, V value) { int intKey = key.hashCode(); V toReturn = null; - if (root==null) { + if (root==null) root = new KeyValueNode(null, intKey, value); - } else { + else toReturn = put(null, root, (byte)0, intKey, value); - } if (toReturn==null) size++; return toReturn; } @@ -143,7 +135,8 @@ private Node find(Node node, int key) { ArrayNode arrayNode = (ArrayNode)node; int position = getPosition(arrayNode.height, key); Node possibleNode = arrayNode.getChild(position); - if (possibleNode==null) return null; + if (possibleNode==null) + return null; return find(possibleNode,key); } return null; @@ -160,8 +153,10 @@ private Node find(int key) { @Override public V get(K key) { Node node = find(key.hashCode()); - if (node==null) return null; - if (node instanceof KeyValueNode) return ((KeyValueNode)node).value; + if (node==null) + return null; + if (node instanceof KeyValueNode) + return ((KeyValueNode)node).value; return null; } @@ -180,8 +175,10 @@ public boolean contains(K key) { @Override public V remove(K key) { Node node = find(key.hashCode()); - if (node==null) return null; - if (node instanceof ArrayNode) return null; + if (node==null) + return null; + if (node instanceof ArrayNode) + return null; KeyValueNode kvNode = (KeyValueNode)node; V value = kvNode.value; @@ -208,6 +205,8 @@ public V remove(K key) { numOfChildren = parent.getNumberOfChildren(); } } + kvNode.key = 0; + kvNode.value = null; size--; return value; } @@ -229,7 +228,7 @@ public int size() { return size; } - private static boolean validate(ArrayNode parent, KeyValueNode child) { + private static boolean validate(ArrayNode parent, KeyValueNode child) { if (parent==null || parent.height==0) return true; int parentPosition = getPosition(parent.height-1,parent.key); @@ -237,7 +236,7 @@ private static boolean validate(ArrayNode parent, KeyValueNode child) return (childPosition==parentPosition); } - private static boolean validate(ArrayNode parent, ArrayNode node) { + private static boolean validate(ArrayNode parent, ArrayNode node) { if (parent!=null) { if (parent.key != (node.parent.key)) return false; if (parent.height+1 != node.height) return false; @@ -285,6 +284,21 @@ public boolean validate() { return true; } + /** + * Convert a big-endian binary string to a little-endian binary string + * and also pads with zeros to make it "BITS" length. + * + * e.g. BITS=5 value==6 big-endian=110 little-endian=011 (pad) return=01100 + */ + private static final String toBinaryString(int value) { + StringBuilder builder = new StringBuilder(Integer.toBinaryString(value)); + builder = builder.reverse(); + while (builder.length()>1)); - if (size>MAX_BITS) size = MAX_BITS; - return size; + newSize = (newSize + (newSize>>1)); + if (newSize>MAX_BITS) newSize = MAX_BITS; + return newSize; } private void addChild(int position, Node child) { @@ -412,6 +428,7 @@ public String toString() { } private static final class KeyValueNode extends Node { + private V value; private KeyValueNode(ArrayNode parent, int key, V value) { @@ -446,7 +463,7 @@ public static String getString(HashArrayMappedTrie tree) { return getString(null, tree.root, -1, "", true); } - private static String getString(Node parent, Node node, int height, String prefix, boolean isTail) { + private static String getString(Node parent, Node node, int height, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); if (node instanceof KeyValueNode) { diff --git a/src/com/jwetherell/algorithms/data_structures/HashMap.java b/src/com/jwetherell/algorithms/data_structures/HashMap.java index 5d37dda9..6556d4b9 100644 --- a/src/com/jwetherell/algorithms/data_structures/HashMap.java +++ b/src/com/jwetherell/algorithms/data_structures/HashMap.java @@ -1,64 +1,765 @@ package com.jwetherell.algorithms.data_structures; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; +import com.jwetherell.algorithms.data_structures.interfaces.IMap; + /** - * Hash Map backed by an array of ArrayLists. hash map is a data structure that + * Hash Map using either chaining or probing. hash map is a data structure that * uses a hash function to map identifying values, known as keys, to their * associated values. - * - * http://en.wikipedia.org/wiki/Hash_table - * + *

+ * @see Hash Map/Table (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") -public class HashMap implements IMap { +public class HashMap implements IMap { + + public static enum Type { CHAINING, PROBING } + + private HashMap delegateMap = null; + + private static class ChainingHashMap extends HashMap { + + private float loadFactor = 10.0f; + private int minimumSize = 1024; + private int initialListSize = 10; + private List>[] array = null; + private int size = 0; + + /** + * Create a hash map with K as the hashing key. + * + * @param size + * initial size. + */ + public ChainingHashMap(int size) { + initializeMap(size); + } + + /** + * Create a hash map with the default hashing key. + */ + public ChainingHashMap() { + initializeMap(minimumSize); + } + + /** + * {@inheritDoc} + */ + @Override + public V put(K key, V value) { + return put(new Pair(key, value)); + } + + public V put(Pair newPair) { + int index = indexOf(newPair.key.hashCode()); + List> list = array[index]; + V prev = null; + boolean exist = false; + // Do not add duplicates + for (Pair p : list) { + if (p.key.equals(newPair.key)) { + prev = p.value; + p.value = newPair.value; + exist = true; + break; + } + } + + if (!exist) + list.add(newPair); + + size++; + + // If size is greater than threshold + int maxSize = (int)(loadFactor*array.length); + if (size >= maxSize) + increase(); + + return prev; + } + + /** + * {@inheritDoc} + */ + @Override + public V get(K key) { + int index = indexOf(key.hashCode()); + List> list = array[index]; + for (Pair p : list) { + if (p.key.equals(key)) + return p.value; + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(K key) { + return (get(key)!=null); + } + + /** + * {@inheritDoc} + */ + @Override + public V remove(K key) { + int index = indexOf(key.hashCode()); + List> list = array[index]; + for (Pair pair : list) { + if (pair.key.equals(key)) { + list.remove(pair); + size--; + + V value = pair.value; + pair.key = null; + pair.value = null; + + int loadFactored = (int)(size/loadFactor); + int smallerSize = getSmallerSize(array.length); + if (loadFactored < smallerSize && smallerSize > minimumSize) + reduce(); + + return value; + } + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() { + for (int i=0; i>[] temp = this.array; + + // Calculate new size and assign + int length = getLargerSize(array.length); + //System.out.println("increase from "+array.length+" to "+length); + initializeMap(length); + + // Re-hash old data + for (List> list : temp) { + for (Pair p :list) { + this.put(p); + } + } + } + + private void reduce() { + // Save old data + List>[] temp = this.array; + + // Calculate new size and check minimum + int length = getSmallerSize(array.length); + //System.out.println("reduce from "+array.length+" to "+length); + initializeMap(length); + + // Re-hash old data + for (List> list : temp) { + for (Pair p :list) { + this.put(p); + } + } + } + + /** + * Increases the input ten-fold. e.g. 16->160 + */ + private static final int getLargerSize(int input) { + return input*10; + } + + /** + * Returns one fourth of the input. e.g. 16->4 + */ + private static final int getSmallerSize(int input) { + return input/4; + } + + /** + * Initialize the hash array. + */ + private void initializeMap(int length) { + this.array = new ArrayList[length]; + for (int i = 0; i < array.length; i++) + this.array[i] = new ArrayList>(initialListSize); + this.size = 0; + } + + /** + * Converts the key into an integer. + * + * @param h + * hash to get index of. + * @return Integer which represents the key. + */ + private int indexOf(int h) { + return h & (array.length-1); + } - private K hashingKey = (K) new Integer(10); - private List>[] array = null; - private int size = 0; + /** + * {@inheritDoc} + */ + @Override + public java.util.Map toMap() { + return (new JavaCompatibleHashMap(this)); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean validate() { + java.util.Set keys = new java.util.HashSet(); + for (List> list : array) { + for (Pair pair : list) { + K k = pair.key; + V v = pair.value; + if (k==null || v==null) return false; + if (keys.contains(k)) return false; + keys.add(k); + } + } + return (keys.size()==size()); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (int key = 0; key < array.length; key++) { + List> list = array[key]; + for (int item = 0; item < list.size(); item++) { + Pair p = list.get(item); + V value = p.value; + if (value != null) builder.append(key).append("=").append(value).append(", "); + } + } + return builder.toString(); + } + + private static class JavaCompatibleHashMap extends java.util.AbstractMap { + + private ChainingHashMap map = null; + + protected JavaCompatibleHashMap(ChainingHashMap map) { + this.map = map; + } + + /** + * {@inheritDoc} + */ + @Override + public V put(K key, V value) { + return map.put(key, value); + } + + /** + * {@inheritDoc} + */ + @Override + public V remove(Object key) { + return map.remove((K)key); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean containsKey(Object key) { + return map.contains((K)key); + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() { + map.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return map.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public java.util.Set> entrySet() { + java.util.Set> set = new java.util.HashSet>() { + + private static final long serialVersionUID = 1L; + + /** + * {@inheritDoc} + */ + @Override + public java.util.Iterator> iterator() { + return (new JavaCompatibleIteratorWrapper(map,super.iterator())); + } + }; + for (List> list : map.array) { + for (Pair p : list) { + java.util.Map.Entry entry = new JavaCompatibleMapEntry(p.key, p.value); + set.add(entry); + } + } + return set; + } + } + } + + private static class ProbingHashMap extends HashMap { + + private int hashingKey = -1; + private float loadFactor = 0.6f; + private int minimumSize = 1024; + private Pair[] array = null; + private int size = 0; + + /** + * Create a hash map with K as the hash. + * + * @param size + * to use for the hash. + */ + public ProbingHashMap(int size) { + initializeMap(size); + } + + /** + * Create a hash map with the default hashing key. + */ + public ProbingHashMap() { + initializeMap(minimumSize); + } + + /** + * {@inheritDoc} + */ + @Override + public V put(K key, V value) { + return put(new Pair(key,value)); + } + + private V put(Pair newPair) { + V prev = null; + int index = indexOf(newPair.key); + + // Check initial position + Pair pair = array[index]; + if (pair == null) { + array[index] = newPair; + size++; + + // If size is greater than threshold + int maxSize = (int)(loadFactor*array.length); + if (size >= maxSize) + increase(); + + return prev; + } + + if (pair.key.equals(newPair.key)) { + prev = pair.value; + pair.value = newPair.value; + return prev; + } + + // Probing until we get back to the starting index + int start = getNextIndex(index); + while (start != index) { + pair = array[start]; + if (pair == null) { + array[start] = newPair; + size++; + + // If size is greater than threshold + int maxSize = (int)(loadFactor*array.length); + if (size >= maxSize) + increase(); + + return prev; + } + + if (pair.key.equals(newPair.key)) { + prev = pair.value; + pair.value = newPair.value; + return prev; + } + + start = getNextIndex(start); + } + // We should never get here. + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public V get(K key) { + int index = indexOf(key); + Pair pair = array[index]; + + // Check initial position + if (pair == null) + return null; + if (pair.key.equals(key)) + return pair.value; + + // Probing until we get back to the starting index + int start = getNextIndex(index); + while (start != index) { + pair = array[start]; + + if (pair == null) + return null; + if (pair.key.equals(key)) + return pair.value; + + start = getNextIndex(start); + } + // If we get here, probing failed. + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(K key) { + return (get(key)!=null); + } + + /** + * {@inheritDoc} + */ + @Override + public V remove(K key) { + int index = indexOf(key); + Pair prev = null; + + // Check initial position + Pair pair = array[index]; + if (pair != null && pair.key.equals(key)) { + prev = array[index]; + array[index] = null; + size--; + + int loadFactored = (int)(size/loadFactor); + int smallerSize = getSmallerSize(array.length); + if (loadFactored < smallerSize && smallerSize > minimumSize) + reduce(); + + return prev.value; + } + + // Probing until we get back to the starting index + int start = getNextIndex(index); + while (start != index) { + pair = array[start]; + if (pair != null && pair.key.equals(key)) { + prev = array[start]; + array[start] = null; + size--; + + int loadFactored = (int)(size/loadFactor); + int smallerSize = getSmallerSize(array.length); + if (loadFactored < smallerSize && smallerSize > minimumSize) + reduce(); + + return prev.value; + } + start = getNextIndex(start); + } + // If we get here, probing failed. + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() { + for (int i=0; i[] temp = this.array; + + // Calculate new size and assign + int length = getLargerSize(array.length); + //System.out.println("increase from "+array.length+" to "+length); + initializeMap(length); + + // Re-hash old data + for (Pair p : temp) { + if (p != null) + this.put(p); + } + } + + private void reduce() { + // Save old data + Pair[] temp = this.array; + + // Calculate new size and check minimum + int length = getSmallerSize(array.length); + //System.out.println("reduce from "+array.length+" to "+length); + initializeMap(length); + + // Re-hash old data + for (Pair p : temp) { + if (p != null) + this.put(p); + } + } + + /** + * Doubles the input. e.g. 16->32 + */ + private static final int getLargerSize(int input) { + return input<<1; + } + + /** + * Returns one fourth of the input. e.g. 16->8->4 + */ + private static final int getSmallerSize(int input) { + return input>>1>>1; + } + + /** + * Returns the next index in the probing sequence, at this point it's linear + */ + private int getNextIndex(int input) { + int i = input+1; + if (i >= array.length) + i = 0; + return i; + } + + /** + * The hashing function. Converts the key into an integer. + * + * @param key + * to create a hash for. + * @return Integer which represents the key. + */ + private int indexOf(K key) { + int k = Math.abs(key.hashCode()) % hashingKey; + if (k >= array.length) + k = k - ((k / array.length) * array.length); + return k; + } + + /** + * {@inheritDoc} + */ + @Override + public java.util.Map toMap() { + return (new JavaCompatibleHashMap(this)); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean validate() { + java.util.Set keys = new java.util.HashSet(); + for (Pair pair : array) { + if (pair == null) + continue; + + K k = pair.key; + V v = pair.value; + if (k==null || v==null) + return false; + if (keys.contains(k)) + return false; + keys.add(k); + } + return (keys.size()==size()); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (int key = 0; key < array.length; key++) { + Pair p = array[key]; + if (p == null) + continue; + V value = p.value; + if (value != null) + builder.append(key).append("=").append(value).append(", "); + } + return builder.toString(); + } + + private static class JavaCompatibleHashMap extends java.util.AbstractMap { + + private ProbingHashMap map = null; + + protected JavaCompatibleHashMap(ProbingHashMap map) { + this.map = map; + } + + /** + * {@inheritDoc} + */ + @Override + public V put(K key, V value) { + return map.put(key, value); + } + + /** + * {@inheritDoc} + */ + @Override + public V remove(Object key) { + return map.remove((K)key); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean containsKey(Object key) { + return map.contains((K)key); + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() { + map.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return map.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public java.util.Set> entrySet() { + java.util.Set> set = new java.util.HashSet>() { + + private static final long serialVersionUID = 1L; + + /** + * {@inheritDoc} + */ + @Override + public java.util.Iterator> iterator() { + return (new JavaCompatibleIteratorWrapper(map,super.iterator())); + } + }; + for (Pair p : map.array) { + if (p==null) + continue; + java.util.Map.Entry entry = new JavaCompatibleMapEntry(p.key, p.value); + set.add(entry); + } + return set; + } + } + } /** * Create a hash map with K as the hashing key. * - * @param key - * to use for the hashing key. + * @param type + * type of hashing to use. + * @param size + * initialize size. */ - public HashMap(K key) { - if (key.intValue()>0) hashingKey = key; - initializeMap(); + public HashMap(Type type, int size) { + if (type == Type.CHAINING) { + delegateMap = new ChainingHashMap(size); + } else if (type == Type.PROBING) { + delegateMap = new ProbingHashMap(size); + } } /** * Create a hash map with the default hashing key. + * + * @param type + * type of hashing to use. */ - public HashMap() { - initializeMap(); + public HashMap(Type type) { + if (type == Type.CHAINING) { + delegateMap = new ChainingHashMap(); + } else if (type == Type.PROBING) { + delegateMap = new ProbingHashMap(); + } } + private HashMap() { } + /** * {@inheritDoc} */ @Override public V put(K key, V value) { - V prev = null; - int hashedKey = hashingFunction(key); - List> list = array[hashedKey]; - boolean exist = false; - // Do not add duplicates - for (Pair p : list) { - if (p.key.equals(key)) { - prev = p.value; - p.value = value; - exist = true; - break; - } - } - if (!exist) - list.add(new Pair(key, value)); - size++; - return prev; + return delegateMap.put(key, value); } /** @@ -66,13 +767,7 @@ public V put(K key, V value) { */ @Override public V get(K key) { - int hashedKey = hashingFunction(key); - List> list = array[hashedKey]; - for (Pair p : list) { - if (p.key.equals(key)) - return p.value; - } - return null; + return delegateMap.get(key); } /** @@ -88,16 +783,7 @@ public boolean contains(K key) { */ @Override public V remove(K key) { - int hashedKey = hashingFunction(key); - List> list = array[hashedKey]; - for (Pair pair : list) { - if (pair.key.equals(key)) { - list.remove(pair); - size--; - return pair.value; - } - } - return null; + return delegateMap.remove(key); } /** @@ -105,10 +791,7 @@ public V remove(K key) { */ @Override public void clear() { - for (int i=0; i>(); - } - size = 0; - } - - /** - * The hashing function. Converts the key into an integer. - * - * @param key - * to create a hash for. - * @return Integer which represents the key. - */ - private int hashingFunction(K key) { - return key.intValue() % hashingKey.intValue(); + return delegateMap.size(); } /** @@ -146,7 +807,7 @@ private int hashingFunction(K key) { */ @Override public java.util.Map toMap() { - return (new JavaCompatibleHashMap(this)); + return delegateMap.toMap(); } /** @@ -154,17 +815,7 @@ public java.util.Map toMap() { */ @Override public boolean validate() { - java.util.Set keys = new java.util.HashSet(); - for (List> list : array) { - for (Pair pair : list) { - K k = pair.key; - V v = pair.value; - if (k==null || v==null) return false; - if (keys.contains(k)) return false; - keys.add(k); - } - } - return (keys.size()==size()); + return delegateMap.validate(); } /** @@ -172,19 +823,10 @@ public boolean validate() { */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); - for (int key = 0; key < array.length; key++) { - List> list = array[key]; - for (int item = 0; item < list.size(); item++) { - Pair p = list.get(item); - V value = p.value; - if (value != null) builder.append(key).append("=").append(value).append(", "); - } - } - return builder.toString(); + return delegateMap.toString(); } - private static final class Pair { + private static final class Pair { private K key = null; private V value = null; @@ -194,17 +836,30 @@ public Pair(K key, V value) { this.value = value; } + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return 31 * (this.key.hashCode()); + } + + /** + * {@inheritDoc} + */ @Override public boolean equals(Object obj) { - if (obj == null) return false; - if (!(obj instanceof Pair)) return false; + if (obj == null) + return false; + if (!(obj instanceof Pair)) + return false; Pair pair = (Pair) obj; return key.equals(pair.key); } } - private static class JavaCompatibleIteratorWrapper implements java.util.Iterator> { + private static class JavaCompatibleIteratorWrapper implements java.util.Iterator> { private HashMap map = null; private java.util.Iterator> iter = null; @@ -220,7 +875,8 @@ public JavaCompatibleIteratorWrapper(HashMap map, java.util.Iterator next() { - if (iter==null) return null; + if (iter==null) + return null; lastEntry = iter.next(); return lastEntry; @@ -240,14 +897,15 @@ public java.util.Map.Entry next() { */ @Override public void remove() { - if (iter==null || lastEntry==null) return; + if (iter==null || lastEntry==null) + return; map.remove(lastEntry.getKey()); iter.remove(); } } - private static class JavaCompatibleMapEntry extends java.util.AbstractMap.SimpleEntry { + private static class JavaCompatibleMapEntry extends java.util.AbstractMap.SimpleEntry { private static final long serialVersionUID = 3282082818647198608L; @@ -255,79 +913,4 @@ public JavaCompatibleMapEntry(K key, V value) { super(key, value); } } - - private static class JavaCompatibleHashMap extends java.util.AbstractMap { - - private HashMap map = null; - - protected JavaCompatibleHashMap(HashMap map) { - this.map = map; - } - - /** - * {@inheritDoc} - */ - @Override - public V put(K key, V value) { - return map.put(key, value); - } - - /** - * {@inheritDoc} - */ - @Override - public V remove(Object key) { - return map.remove((K)key); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean containsKey(Object key) { - return map.contains((K)key); - } - - /** - * {@inheritDoc} - */ - @Override - public void clear() { - map.clear(); - } - - /** - * {@inheritDoc} - */ - @Override - public int size() { - return map.size(); - } - - /** - * {@inheritDoc} - */ - @Override - public java.util.Set> entrySet() { - java.util.Set> set = new java.util.HashSet>() { - - private static final long serialVersionUID = 1L; - - /** - * {@inheritDoc} - */ - @Override - public java.util.Iterator> iterator() { - return (new JavaCompatibleIteratorWrapper(map,super.iterator())); - } - }; - for (List> list : map.array) { - for (Pair p : list) { - java.util.Map.Entry entry = new JavaCompatibleMapEntry(p.key, p.value); - set.add(entry); - } - } - return set; - } - } } diff --git a/src/com/jwetherell/algorithms/data_structures/ImplicitKeyTreap.java b/src/com/jwetherell/algorithms/data_structures/ImplicitKeyTreap.java new file mode 100644 index 00000000..bc98df10 --- /dev/null +++ b/src/com/jwetherell/algorithms/data_structures/ImplicitKeyTreap.java @@ -0,0 +1,655 @@ +package com.jwetherell.algorithms.data_structures; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.jwetherell.algorithms.data_structures.interfaces.IList; + +/** + * A Treap is a self-balancing binary search tree that uses randomization to maintain + * a low height. In this version, it is used emulate the operations of an array and linked list. + *

+ * Time Complexity: Assuming the join/merge functions have constant complexity. + * add(value), add(index,value), remove(index), set(index,value), get(index) all have O(log N). + * remove(value), get(value), contains(value) all have O(N). + *

+ * Space Complexity: O(N) + *

+ * Note: This implementation is 0-based, meaning that all + * indices from 0 to size() - 1, inclusive, are accessible. + *

+ * @see Treap (Wikipedia) + *
+ * @author Justin Wetherell + */ +@SuppressWarnings("unchecked") +public class ImplicitKeyTreap implements IList { + + private static final Random RANDOM = new Random(); + + private static int randomSeed = Integer.MAX_VALUE; // This should be at least twice the number of Nodes + + protected Node root = null; + protected int size = 0; + + /** + * Default constructor. + */ + public ImplicitKeyTreap() { } + + /** + * Constructor with a random seed. + * + * @param randomSeed to use. + */ + public ImplicitKeyTreap(int randomSeed) { + this(); + ImplicitKeyTreap.randomSeed = randomSeed; + } + + /** + * {@inheritDoc} + * + * Note: adds to the end of the treap (rightmost node) + */ + @Override + public boolean add(T value) { + final T t = add(size, value); + return (t!=null); + } + + /** + * Insert value at index + * + * @param index to insert value + * @param value to insert + */ + public T add(int index, T value) { + addAtIndexAndUpdate(index, value); + + // Check to make sure value was added + final Node n = getNodeByIndex(index); + if (n == null) + return null; + return n.value; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean remove(T value) { + // See if value already exists + final int idx = getIndexByValue(value); + if (idx < 0) + return false; + + final T t = removeAtIndex(idx); + return (t!=null); + } + + /** + * Remove value at index + * + * @param index to remove value + * @return value or null if not found + */ + public T removeAtIndex(int index) { + // See if index already exists + Node n = getNodeByIndex(index); + if (n == null) + return null; + + removeAtIndexAndUpdate(index); + return n.value; + } + + /** + * Set value at index + * + * @param index to remove value + * @return value or null if not found + */ + public T set(int index, T value) { + // See if index already exists + final Node n = getNodeByIndex(index); + if (n == null) + return null; + + n.value = value; + return n.value; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(T value) { + final Node n = getNodeByValue(value); + return (n!=null); + } + + /** + * Get value at index + * + * @param index to remove value + */ + public T getAtIndex(int index) { + final Node n = getNodeByIndex(index); + if (n == null) + return null; + return n.value; + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() { + root = null; + size = 0; + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return size; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean validate() { + if (root == null) + return true; + return validateNode(root); + } + + private boolean validateNode(Node node) { + final Node left = node.left; + final Node right = node.right; + + if (left != null) { + if (node.priority < left.priority) + return validateNode(left); + return false; + } + if (right != null) { + if (node.priority < right.priority) + return validateNode(right); + return false; + } + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public List toList() { + return (new JavaCompatibleArrayList(this)); + } + + /** + * {@inheritDoc} + */ + @Override + public java.util.Collection toCollection() { + return (new JavaCompatibleArrayList(this)); + } + + /** + * Split the treap at index + * + * @param index to split at + * + * @return Pair which contains root of both trees + */ + public Pair split(int index) { + final Pair p = split((Node)root, index); + if (p.left != null) + p.left.parent = null; + if (p.right != null) + p.right.parent = null; + return p; + } + + public void addAtIndexAndUpdate(int index, T value) { + root = insert(((Node)root), index, value); + if (root == null) + size = 0; + else + size = (((Node)root).size); + } + + private void removeAtIndexAndUpdate(int index) { + root = remove(((Node)root), index); + if (root == null) + size = 0; + else + size = (((Node)root).size); + } + + private Node getNodeByValue(T value) { + return getNodeByValue(root,value); + } + + private Node getNodeByIndex(int index) { + if (root == null) + return null; + + final Node l = (Node)root.left; + final Node r = (Node)root.right; + final int leftSize = ((l!=null)?l.size:0); + final int idx = leftSize; + + if (idx == index) { + return root; + } else if (index < leftSize) { + return getNodeByIndex(l, idx, index); + } else { + return getNodeByIndex(r, idx, index); + } + } + + private int getIndexByValue(T value) { + final Node node = (Node)root; + if (value == null || node == null) + return Integer.MIN_VALUE; + + final Node l = (Node)node.left; + final Node r = (Node)node.right; + final int leftSize = ((l!=null)?l.size:0); + final int idx = leftSize; + + if (value.equals(node.value)) + return idx; + + int i = getIndexByValue(l, idx, value); + if (i >= 0) + return i; + + i = getIndexByValue(r, idx, value); + return i; + } + + /** + * Split the treap rooted at node at given index + * + * @param node which represents root + * @param index in treap to split + * + * @return Pair which contains root of both trees + */ + public static Pair split(Node node, int index) { + if (node == null) + return new Pair(null, null); + + final int leftSize = (node.left!=null? + ((Node)node.left).size + : + 0); + if (index <= leftSize) { + final Pair sub = split((Node)node.left, index); + node.left = sub.right; + if (node.left != null) + node.left.parent = node; + sub.right = node; + node.update(); + return sub; + } + // else + final Pair sub = split((Node)node.right, (index - leftSize - 1)); + node.right = sub.left; + if (node.right != null) + node.right.parent = node; + sub.left = node; + node.update(); + return sub; + } + + /** + * Merge treaps from given left and right nodes + * + * @param left node which represents root of left treap + * @param right node which represents root of great treap + * + * @return treap from merged treaps + */ + public static Node merge(Node left, Node right) { + if (left == null) + return right; + + if (right == null) + return left; + + if (left.priority < right.priority) { + left.right = merge((Node)left.right, right); + if (left.right != null) + left.right.parent = left; + left.update(); + return left; + } + // else + right.left = merge(left, (Node)right.left); + if (right.left != null) + right.left.parent = right; + right.update(); + return right; + } + + private static Node insert(Node root, int index, T value) { + final Pair p = split(root, index); + return merge(merge((Node)p.left, new Node(value)), (Node)p.right); + } + + private static Node remove(Node root, int index) { + final Pair p = split(root, index); + final int leftSize = (p.left!=null? + ((Node)p.left).size + : + 0); + return merge(p.left, (split(p.right, (index + 1 - leftSize))).right); + } + + private static Node getNodeByValue(Node node, T value) { + if (node == null) + return null; + + if (node.value.equals(value)) + return node; + + Node n = getNodeByValue(node.left, value); + if (n == null) + n = getNodeByValue(node.right, value); + return n; + } + + private static Node getNodeByIndex(Node node, int parentIndex, int index) { + if (node == null) + return null; + + final Node p = (Node)node.parent; + final Node l = (Node)node.left; + final Node r = (Node)node.right; + final int leftSize = ((l!=null)?l.size:0); + final int rightSize = ((r!=null)?r.size:0); + + int idx = Integer.MIN_VALUE; + if (p!=null && node.equals(p.left)) { + // left + idx = parentIndex - rightSize - 1; + } else if (p!=null && node.equals(p.right)) { + // right + idx = leftSize + parentIndex + 1; + } else { + throw new RuntimeException("I do not have a parent :-("); + } + + if (idx == index) + return node; + + if (index <= idx) { + return getNodeByIndex(l, idx, index); + } else { + return getNodeByIndex(r, idx, index); + } + } + + private static int getIndexByValue(Node node, int parentIndex, T value) { + if (node == null) + return Integer.MIN_VALUE; + + final Node p = (Node)node.parent; + final Node l = (Node)node.left; + final Node r = (Node)node.right; + final int leftSize = ((l!=null)?l.size:0); + final int rightSize = ((r!=null)?r.size:0); + + int idx = Integer.MIN_VALUE; + if (p!=null && node.equals(p.left)) { + // left + idx = parentIndex - rightSize - 1; + } else if (p!=null && node.equals(p.right)) { + // right + idx = leftSize + parentIndex + 1; + } else { + throw new RuntimeException("I do not have a parent :-("); + } + + if (value.equals(node.value)) + return idx; + + int i = getIndexByValue(l, idx, value); + if (i >= 0) + return i; + + i = getIndexByValue(r, idx, value); + return i; + } + + public T[] inOrder() { + return inOrder(root,size); + } + + public static T[] inOrder(Node node, int size) { + T[] data = (T[]) new Object[size]; + if (node == null) + return data; + + inOrder(node, data, 0); + return data; + } + + private static int inOrder(Node node, T[] data, int idx) { + if (node == null) + return idx; + + idx = inOrder(node.left, data, idx); + data[idx++] = node.value; + idx = inOrder(node.right, data, idx); + return idx; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return TreePrinter.getString(this); + } + + public static class Node { + + private T value = null; + private int priority; + private int size; + private Node parent = null; + private Node left = null; + private Node right = null; + + private Node(T id) { + this(null, id); + } + + private Node(Node parent, T id) { + this.parent = parent; + this.value = id; + this.size = 1; + this.priority = RANDOM.nextInt(randomSeed); + } + + public int getSize() { + return size; + } + + private void update() { + size = 1 + (left!=null? + ((Node)left).size + : + 0) + + (right!=null? + ((Node)right).size + : + 0); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "id=" + value + " parent=" + ((parent != null) ? parent.value : "NULL") + " left=" + + ((left != null) ? left.value : "NULL") + " right=" + ((right != null) ? right.value : "NULL"); + } + } + + public static class Pair { + + private Node left; + private Node right; + + private Pair(Node left, Node right) { + this.left = left; + this.right = right; + } + + public Node getLesser() { + return left; + } + + public Node getGreater() { + return right; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "left={"+left.toString()+"} right={"+right.toString()+"}"; + } + } + + public static class JavaCompatibleArrayList extends java.util.AbstractList implements java.util.RandomAccess { + + private ImplicitKeyTreap list = null; + + public JavaCompatibleArrayList(ImplicitKeyTreap list) { + this.list = list; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean add(T value) { + return list.add(value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean remove(Object value) { + return list.remove((T)value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Object value) { + return list.contains((T)value); + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return list.size; + } + + /** + * {@inheritDoc} + */ + @Override + public void add(int index, T value) { + list.add(index, value); + } + + /** + * {@inheritDoc} + */ + @Override + public T remove(int index) { + return list.removeAtIndex(index); + } + + /** + * {@inheritDoc} + */ + @Override + public T get(int index) { + T t = list.getAtIndex(index); + if (t!=null) + return t; + throw new IndexOutOfBoundsException(); + } + + /** + * {@inheritDoc} + */ + @Override + public T set(int index, T value) { + return list.set(index, value); + } + } + + protected static class TreePrinter { + + public static String getString(ImplicitKeyTreap tree) { + if (tree.root == null) + return "Tree has no nodes."; + return getString(tree.root, "", true); + } + + private static String getString(Node node, String prefix, boolean isTail) { + StringBuilder builder = new StringBuilder(); + + if (node.parent != null) { + String side = "left"; + if (node.equals(node.parent.right)) + side = "right"; + builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + side + ") " + node.value + "\n"); + } else { + builder.append(prefix + (isTail ? "└── " : "├── ") + node.value + "\n"); + } + List> children = null; + if (node.left != null || node.right != null) { + children = new ArrayList>(2); + if (node.left != null) + children.add(node.left); + if (node.right != null) + children.add(node.right); + } + if (children != null) { + for (int i = 0; i < children.size() - 1; i++) { + builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false)); + } + if (children.size() >= 1) { + builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); + } + } + + return builder.toString(); + } + } +} + diff --git a/src/com/jwetherell/algorithms/data_structures/IntervalSum.java b/src/com/jwetherell/algorithms/data_structures/IntervalSum.java new file mode 100644 index 00000000..b4c62f57 --- /dev/null +++ b/src/com/jwetherell/algorithms/data_structures/IntervalSum.java @@ -0,0 +1,165 @@ +package com.jwetherell.algorithms.data_structures; + +import java.util.List; +import java.util.ArrayList; + +/** + * Implementation of array holding integer values which allows to count sum of elements in given + * interval in O(log n) complexity. + *
+ * @author Szymon Stankiewicz + * @author Justin Wetherell + */ +public class IntervalSum { + + private List values = new ArrayList(); + private List prefSums = new ArrayList(); + + /** + * Creates empty IntervalSumArray + */ + public IntervalSum() { + values.add(0); + prefSums.add(0); + } + + /** + * Creates IntervalSumArray of given size filled with zeros. + * + * Complexity O(size). + * + * @param size size of IntervalSumArray + */ + public IntervalSum(int size) { + for (int i = 0; i values) { + for (Integer v: values) + add(v); + } + + private static int greatestPowerOfTwoDividing(int x) { + return x & (-x); + } + + private static int successor(int x) { + return x + greatestPowerOfTwoDividing(x); + } + + private static int predecessor(int x) { + return x - greatestPowerOfTwoDividing(x); + } + + /** + * @return size of IntervalSumArray + */ + public int size() { + return prefSums.size() - 1; + } + + /** + * Adds value at the end of IntervalSumArray. + * + * Complexity O(log n). + * + * @param val value to be added at the end of array. + */ + public void add(int val) { + values.add(val); + for (int i = 1; i= size()) + throw new IndexOutOfBoundsException(); + index++; + int diff = val - values.get(index); + values.set(index, val); + while (index <= size()) { + int oldPrefSum = prefSums.get(index); + prefSums.set(index, oldPrefSum + diff); + index = successor(index); + } + } + + /** + * Return value with given index. + * + * Complexity O(1) + * + * @param index index of array. + * @return value at given index. + */ + public int get(int index) { + return values.get(index+1); + } + + /** + * Return sum of values from 0 to end inclusively. + * + * Complexity O(log n) + * + * @param end end of interval + * @return sum of values in interval + */ + public int sum(int end) { + if (end < 0 || end >= size()) + throw new IndexOutOfBoundsException(); + end++; + int s = 0; + while (end > 0) { + s += prefSums.get(end); + end = predecessor(end); + } + return s; + } + + /** + * Return sum of all values inclusively. + * + * Complexity O(log n) + * + * @return sum of values in array + */ + public int sum() { + return sum(size()-1); + } + + /** + * Return sum of values from start to end inclusively. + * + * Complexity O(log n) + * + * @param start start of interval + * @param end end of interval + * @return sum of values in interval + */ + public int sum(int start, int end) { + if (start > end) + throw new IllegalArgumentException("Start must be less then end"); + int startPrefSum = start == 0 ? 0 : sum(start-1); + int endPrefSum = sum(end); + return endPrefSum - startPrefSum; + } +} diff --git a/src/com/jwetherell/algorithms/data_structures/IntervalTree.java b/src/com/jwetherell/algorithms/data_structures/IntervalTree.java index 7e98245a..1a82ef30 100644 --- a/src/com/jwetherell/algorithms/data_structures/IntervalTree.java +++ b/src/com/jwetherell/algorithms/data_structures/IntervalTree.java @@ -1,29 +1,27 @@ package com.jwetherell.algorithms.data_structures; -import java.security.InvalidParameterException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.Iterator; +import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.TreeSet; /** * An interval tree is an ordered tree data structure to hold intervals. * Specifically, it allows one to efficiently find all intervals that overlap - * with any given interval or point. - * - * http://en.wikipedia.org/wiki/Interval_tree - * + * with any given interval or point. + *

+ * @see Interval Tree (Wikipedia) + *
* @author Justin Wetherell */ public class IntervalTree { private Interval root = null; - private static final Comparator> startComparator = new Comparator>() { - + private static final Comparator> START_COMPARATOR = new Comparator>() { /** * {@inheritDoc} */ @@ -38,17 +36,16 @@ public int compare(IntervalData arg0, IntervalData arg1) { } }; - private static final Comparator> endComparator = new Comparator>() { - + private static final Comparator> END_COMPARATOR = new Comparator>() { /** * {@inheritDoc} */ @Override public int compare(IntervalData arg0, IntervalData arg1) { // Compare end first - if (arg0.end < arg1.end) + if (arg0.end > arg1.end) return -1; - if (arg1.end < arg0.end) + if (arg1.end > arg0.end) return 1; return 0; } @@ -73,26 +70,29 @@ protected static final Interval createFromList(List middle = intervals.get(0); newInterval.center = ((middle.start + middle.end) / 2); newInterval.add(middle); - } else { - int half = intervals.size() / 2; - IntervalData middle = intervals.get(half); - newInterval.center = ((middle.start + middle.end) / 2); - List> leftIntervals = new ArrayList>(); - List> rightIntervals = new ArrayList>(); - for (IntervalData interval : intervals) { - if (interval.end < newInterval.center) { - leftIntervals.add(interval); - } else if (interval.start > newInterval.center) { - rightIntervals.add(interval); - } else { - newInterval.add(interval); - } - } - if (leftIntervals.size() > 0) - newInterval.left = createFromList(leftIntervals); - if (rightIntervals.size() > 0) - newInterval.right = createFromList(rightIntervals); + return newInterval; } + + int half = intervals.size() / 2; + IntervalData middle = intervals.get(half); + newInterval.center = ((middle.start + middle.end) / 2); + List> leftIntervals = new ArrayList>(); + List> rightIntervals = new ArrayList>(); + for (IntervalData interval : intervals) { + if (interval.end < newInterval.center) { + leftIntervals.add(interval); + } else if (interval.start > newInterval.center) { + rightIntervals.add(interval); + } else { + newInterval.add(interval); + } + } + + if (leftIntervals.size() > 0) + newInterval.left = createFromList(leftIntervals); + if (rightIntervals.size() > 0) + newInterval.right = createFromList(rightIntervals); + return newInterval; } @@ -148,13 +148,10 @@ private static String getString(Interval interval, String if (interval.right != null) children.add(interval.right); if (children.size() > 0) { - for (int i = 0; i < children.size() - 1; i++) { + for (int i = 0; i < children.size() - 1; i++) builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false)); - } - if (children.size() > 0) { - builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), - true)); - } + if (children.size() > 0) + builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } return builder.toString(); @@ -166,11 +163,10 @@ public static final class Interval { private long center = Long.MIN_VALUE; private Interval left = null; private Interval right = null; - private List> overlap = new ArrayList>(); // startComparator + private List> overlap = new ArrayList>(); private void add(IntervalData data) { overlap.add(data); - Collections.sort(overlap,startComparator); } /** @@ -184,6 +180,7 @@ public IntervalData query(long index) { IntervalData results = null; if (index < center) { // overlap is sorted by start point + Collections.sort(overlap,START_COMPARATOR); for (IntervalData data : overlap) { if (data.start > index) break; @@ -191,31 +188,30 @@ public IntervalData query(long index) { IntervalData temp = data.query(index); if (results == null && temp != null) results = temp; - else if (temp != null) + else if (results != null && temp != null) results.combined(temp); } } else if (index >= center) { - // overlapEnd is sorted by end point - List> overlapEnd = new ArrayList>(); - Collections.sort(overlapEnd,endComparator); - overlapEnd.addAll(overlap); - for (IntervalData data : overlapEnd) { + // overlap is reverse sorted by end point + Collections.sort(overlap,END_COMPARATOR); + for (IntervalData data : overlap) { if (data.end < index) break; IntervalData temp = data.query(index); if (results == null && temp != null) results = temp; - else if (temp != null) + else if (results != null && temp != null) results.combined(temp); } } + if (index < center) { if (left != null) { IntervalData temp = left.query(index); if (results == null && temp != null) results = temp; - else if (temp != null) + else if (results != null && temp != null) results.combined(temp); } } else if (index >= center) { @@ -223,7 +219,7 @@ else if (temp != null) IntervalData temp = right.query(index); if (results == null && temp != null) results = temp; - else if (temp != null) + else if (results != null && temp != null) results.combined(temp); } } @@ -247,23 +243,26 @@ public IntervalData query(long start, long end) { IntervalData temp = data.query(start, end); if (results == null && temp != null) results = temp; - else if (temp != null) + else if (results != null && temp != null) results.combined(temp); } + if (left != null && start < center) { IntervalData temp = left.query(start, end); if (temp != null && results == null) results = temp; - else if (temp != null) + else if (results != null && temp != null) results.combined(temp); } + if (right != null && end >= center) { IntervalData temp = right.query(start, end); if (temp != null && results == null) results = temp; - else if (temp != null) + else if (results != null && temp != null) results.combined(temp); } + return results; } @@ -282,11 +281,11 @@ public String toString() { /** * Data structure representing an interval. */ - public static final class IntervalData implements Comparable> { + public static class IntervalData implements Comparable> { private long start = Long.MIN_VALUE; private long end = Long.MAX_VALUE; - private Set set = new TreeSet(); // Sorted + private Set set = new HashSet(); /** * Interval data using O as it's unique identifier @@ -315,24 +314,40 @@ public IntervalData(long start, long end, O object) { /** * Interval data list which should all be unique * - * @param list + * @param set * of interval data objects */ public IntervalData(long start, long end, Set set) { this.start = start; this.end = end; this.set = set; + } - // Make sure they are unique - Iterator iter = set.iterator(); - while (iter.hasNext()) { - O obj1 = iter.next(); - O obj2 = null; - if (iter.hasNext()) - obj2 = iter.next(); - if (obj1.equals(obj2)) - throw new InvalidParameterException("Each interval data in the list must be unique."); - } + /** + * Get the start of this interval + * + * @return Start of interval + */ + public long getStart() { + return start; + } + + /** + * Get the end of this interval + * + * @return End of interval + */ + public long getEnd() { + return end; + } + + /** + * Get the data set in this interval + * + * @return Unmodifiable collection of data objects + */ + public Collection getData() { + return Collections.unmodifiableCollection(this.set); } /** @@ -366,55 +381,59 @@ public IntervalData combined(IntervalData data) { * @return deep copy. */ public IntervalData copy() { - Set listCopy = new TreeSet(); - listCopy.addAll(set); - return new IntervalData(start, end, listCopy); + Set copy = new HashSet(); + copy.addAll(set); + return new IntervalData(start, end, copy); } /** * Query inside this data object. * - * @param start - * of range to query for. - * @param end - * of range to query for. + * @param index + * to find Data. + * * @return Data queried for or NULL if it doesn't match the query. */ public IntervalData query(long index) { - if (index < this.start || index > this.end) { - // Ignore - } else { - return copy(); - } - return null; + if (index < this.start || index > this.end) + return null; + + return copy(); } /** * Query inside this data object. * - * @param start + * @param startOfQuery * of range to query for. - * @param end + * @param endOfQuery * of range to query for. * @return Data queried for or NULL if it doesn't match the query. */ - public IntervalData query(long start, long end) { - if (end < this.start || start > this.end) { - // Ignore - } else { - return copy(); - } - return null; + public IntervalData query(long startOfQuery, long endOfQuery) { + if (endOfQuery < this.start || startOfQuery > this.end) + return null; + + return copy(); } /** * {@inheritDoc} */ @Override + public int hashCode() { + return 31 * ((int)(this.start + this.end)) + this.set.size(); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + @Override public boolean equals(Object obj) { if (!(obj instanceof IntervalData)) return false; - @SuppressWarnings("unchecked") + IntervalData data = (IntervalData) obj; if (this.start == data.start && this.end == data.end) { if (this.set.size() != data.set.size()) diff --git a/src/com/jwetherell/algorithms/data_structures/KdTree.java b/src/com/jwetherell/algorithms/data_structures/KdTree.java index 5cf1b998..b7558302 100644 --- a/src/com/jwetherell/algorithms/data_structures/KdTree.java +++ b/src/com/jwetherell/algorithms/data_structures/KdTree.java @@ -1,10 +1,16 @@ package com.jwetherell.algorithms.data_structures; +import static java.lang.Math.cos; +import static java.lang.Math.sin; + +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Deque; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -15,18 +21,17 @@ * useful data structure for several applications, such as searches involving a * multidimensional search key (e.g. range searches and nearest neighbor * searches). k-d trees are a special case of binary space partitioning trees. - * - * http://en.wikipedia.org/wiki/K-d_tree - * + *

+ * @see K-D Tree (Wikipedia) + *
* @author Justin Wetherell */ -public class KdTree { +public class KdTree implements Iterable { private int k = 3; private KdNode root = null; private static final Comparator X_COMPARATOR = new Comparator() { - /** * {@inheritDoc} */ @@ -41,7 +46,6 @@ public int compare(XYZPoint o1, XYZPoint o2) { }; private static final Comparator Y_COMPARATOR = new Comparator() { - /** * {@inheritDoc} */ @@ -56,7 +60,6 @@ public int compare(XYZPoint o1, XYZPoint o2) { }; private static final Comparator Z_COMPARATOR = new Comparator() { - /** * {@inheritDoc} */ @@ -77,23 +80,37 @@ public int compare(XYZPoint o1, XYZPoint o2) { /** * Default constructor. */ - public KdTree() { - } + public KdTree() { } /** * Constructor for creating a more balanced tree. It uses the * "median of points" algorithm. - * + * * @param list * of XYZPoints. */ public KdTree(List list) { + super(); root = createNode(list, k, 0); } /** - * Create node from list of XYZPoints. - * + * Constructor for creating a more balanced tree. It uses the + * "median of points" algorithm. + * + * @param list + * of XYZPoints. + * @param k + * of the tree. + */ + public KdTree(List list, int k) { + super(); + root = createNode(list, k, 0); + } + + /** + * Creates node from list of XYZPoints. + * * @param list * of XYZPoints. * @param k @@ -115,40 +132,32 @@ else if (axis == Y_AXIS) Collections.sort(list, Z_COMPARATOR); KdNode node = null; + List less = new ArrayList(list.size()); + List more = new ArrayList(list.size()); if (list.size() > 0) { int medianIndex = list.size() / 2; - node = new KdNode(k, depth, list.get(medianIndex)); - List less = new ArrayList(list.size() - 1); - List more = new ArrayList(list.size() - 1); + node = new KdNode(list.get(medianIndex), k, depth); // Process list to see where each non-median point lies for (int i = 0; i < list.size(); i++) { if (i == medianIndex) continue; XYZPoint p = list.get(i); + // Cannot assume points before the median are less since they could be equal if (KdNode.compareTo(depth, k, p, node.id) <= 0) { less.add(p); } else { more.add(p); } } - if ((medianIndex - 1) >= 0) { - // Cannot assume points before the median are less since they - // could be equal - // List less = list.subList(0, mediaIndex); - if (less.size() > 0) { - node.lesser = createNode(less, k, depth + 1); - node.lesser.parent = node; - } + + if ((medianIndex-1 >= 0) && less.size() > 0) { + node.lesser = createNode(less, k, depth + 1); + node.lesser.parent = node; } - if ((medianIndex + 1) <= (list.size() - 1)) { - // Cannot assume points after the median are less since they - // could be equal - // List more = list.subList(mediaIndex + 1, - // list.size()); - if (more.size() > 0) { - node.greater = createNode(more, k, depth + 1); - node.greater.parent = node; - } + + if ((medianIndex <= list.size()-1) && more.size() > 0) { + node.greater = createNode(more, k, depth + 1); + node.greater.parent = node; } } @@ -156,8 +165,8 @@ else if (axis == Y_AXIS) } /** - * Add value to the tree. Tree can contain multiple equal values. - * + * Adds value to the tree. Tree can contain multiple equal values. + * * @param value * T to add to the tree. * @return True if successfully added to tree. @@ -176,7 +185,7 @@ public boolean add(T value) { if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) { // Lesser if (node.lesser == null) { - KdNode newNode = new KdNode(k, node.depth + 1, value); + KdNode newNode = new KdNode(value, k, node.depth + 1); newNode.parent = node; node.lesser = newNode; break; @@ -185,7 +194,7 @@ public boolean add(T value) { } else { // Greater if (node.greater == null) { - KdNode newNode = new KdNode(k, node.depth + 1, value); + KdNode newNode = new KdNode(value, k, node.depth + 1); newNode.parent = node; node.greater = newNode; break; @@ -199,13 +208,13 @@ public boolean add(T value) { /** * Does the tree contain the value. - * + * * @param value * T to locate in the tree. * @return True if tree contains value. */ public boolean contains(T value) { - if (value == null) + if (value == null || root == null) return false; KdNode node = getNode(this, value); @@ -213,8 +222,8 @@ public boolean contains(T value) { } /** - * Locate T in the tree. - * + * Locates T in the tree. + * * @param tree * to search. * @param value @@ -246,14 +255,14 @@ private static final KdNode getNode(KdTree tree, } /** - * Remove first occurrence of value in the tree. - * + * Removes first occurrence of value in the tree. + * * @param value * T to remove from the tree. * @return True if value was removed from the tree. */ public boolean remove(T value) { - if (value == null) + if (value == null || root == null) return false; KdNode node = getNode(this, value); @@ -296,8 +305,8 @@ public boolean remove(T value) { } /** - * Get the (sub) tree rooted at root. - * + * Gets the (sub) tree rooted at root. + * * @param root * of tree to get nodes for. * @return points in (sub) tree, not including root. @@ -319,20 +328,20 @@ private static final List getTree(KdNode root) { return list; } - /** - * K Nearest Neighbor search - * + /** + * Searches the K nearest neighbor. + * * @param K * Number of neighbors to retrieve. Can return more than K, if * last nodes are equal distances. * @param value * to find neighbors of. - * @return collection of T neighbors. + * @return Collection of T neighbors. */ @SuppressWarnings("unchecked") public Collection nearestNeighbourSearch(int K, T value) { - if (value == null) - return null; + if (value == null || root == null) + return Collections.EMPTY_LIST; // Map used for results TreeSet results = new TreeSet(new EuclideanComparator(value)); @@ -368,14 +377,12 @@ public Collection nearestNeighbourSearch(int K, T value) { // Load up the collection of the results Collection collection = new ArrayList(K); - for (KdNode kdNode : results) { + for (KdNode kdNode : results) collection.add((T) kdNode.id); - } return collection; } - private static final void searchNode(T value, KdNode node, int K, - TreeSet results, Set examined) { + private static final void searchNode(T value, KdNode node, int K, TreeSet results, Set examined) { examined.add(node); // Search node @@ -448,6 +455,24 @@ private static final void searchNode(T value, KdNode } } + /** + * Adds, in a specified queue, a given node and its related nodes (lesser, greater). + * + * @param node + * Node to check. May be null. + * + * @param results + * Queue containing all found entries. Must not be null. + */ + @SuppressWarnings("unchecked") + private static void search(final KdNode node, final Deque results) { + if (node != null) { + results.add((T) node.id); + search(node.greater, results); + search(node.lesser, results); + } + } + /** * {@inheritDoc} */ @@ -458,7 +483,7 @@ public String toString() { protected static class EuclideanComparator implements Comparator { - private XYZPoint point = null; + private final XYZPoint point; public EuclideanComparator(XYZPoint point) { this.point = point; @@ -477,23 +502,50 @@ else if (d2.compareTo(d1) < 0) return 1; return o1.id.compareTo(o2.id); } - }; + } + + /** + * Searches all entries from the first to the last entry. + * + * @return Iterator + * allowing to iterate through a collection containing all found entries. + */ + public Iterator iterator() { + final Deque results = new ArrayDeque(); + search(root, results); + return results.iterator(); + } + + /** + * Searches all entries from the last to the first entry. + * + * @return Iterator + * allowing to iterate through a collection containing all found entries. + */ + public Iterator reverse_iterator() { + final Deque results = new ArrayDeque(); + search(root, results); + return results.descendingIterator(); + } public static class KdNode implements Comparable { - private int k = 3; - private int depth = 0; - private XYZPoint id = null; + private final XYZPoint id; + private final int k; + private final int depth; + private KdNode parent = null; private KdNode lesser = null; private KdNode greater = null; public KdNode(XYZPoint id) { this.id = id; + this.k = 3; + this.depth = 0; } - public KdNode(int k, int depth, XYZPoint id) { - this(id); + public KdNode(XYZPoint id, int k, int depth) { + this.id = id; this.k = k; this.depth = depth; } @@ -507,6 +559,14 @@ public static int compareTo(int depth, int k, XYZPoint o1, XYZPoint o2) { return Z_COMPARATOR.compare(o1, o2); } + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return 31 * (this.k + this.depth + this.id.hashCode()); + } + /** * {@inheritDoc} */ @@ -546,25 +606,59 @@ public String toString() { public static class XYZPoint implements Comparable { - private double x = Double.NEGATIVE_INFINITY; - private double y = Double.NEGATIVE_INFINITY; - private double z = Double.NEGATIVE_INFINITY; + protected final double x; + protected final double y; + protected final double z; + /** + * z is defaulted to zero. + * + * @param x + * @param y + */ public XYZPoint(double x, double y) { this.x = x; this.y = y; this.z = 0; } + /** + * Default constructor + * + * @param x + * @param y + * @param z + */ public XYZPoint(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } + /** + * Does not use R to calculate x, y, and z. Where R is the approximate radius of earth (e.g. 6371KM). + * @param latitude + * @param longitude + */ + public XYZPoint(Double latitude, Double longitude) { + x = cos(Math.toRadians(latitude)) * cos(Math.toRadians(longitude)); + y = cos(Math.toRadians(latitude)) * sin(Math.toRadians(longitude)); + z = sin(Math.toRadians(latitude)); + } + + public double getX() { + return x; + } + public double getY() { + return y; + } + public double getZ() { + return z; + } + /** * Computes the Euclidean distance from this point to the other. - * + * * @param o1 * other point. * @return euclidean distance. @@ -575,7 +669,7 @@ public double euclideanDistance(XYZPoint o1) { /** * Computes the Euclidean distance from one point to the other. - * + * * @param o1 * first point. * @param o2 @@ -584,7 +678,15 @@ public double euclideanDistance(XYZPoint o1) { */ private static final double euclideanDistance(XYZPoint o1, XYZPoint o2) { return Math.sqrt(Math.pow((o1.x - o2.x), 2) + Math.pow((o1.y - o2.y), 2) + Math.pow((o1.z - o2.z), 2)); - }; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return 31 * (int)(this.x + this.y + this.z); + } /** * {@inheritDoc} @@ -597,7 +699,13 @@ public boolean equals(Object obj) { return false; XYZPoint xyzPoint = (XYZPoint) obj; - return compareTo(xyzPoint) == 0; + if (Double.compare(this.x, xyzPoint.x)!=0) + return false; + if (Double.compare(this.y, xyzPoint.y)!=0) + return false; + if (Double.compare(this.z, xyzPoint.z)!=0) + return false; + return true; } /** diff --git a/src/com/jwetherell/algorithms/data_structures/LCPArray.java b/src/com/jwetherell/algorithms/data_structures/LCPArray.java new file mode 100644 index 00000000..9dd1632d --- /dev/null +++ b/src/com/jwetherell/algorithms/data_structures/LCPArray.java @@ -0,0 +1,78 @@ +package com.jwetherell.algorithms.data_structures; + +import java.util.ArrayList; + +/** + * In computer science, the longest common prefix array (LCP array) is an auxiliary + * data structure to the suffix array. It stores the lengths of the longest common + * prefixes (LCPs) between all pairs of consecutive suffixes in a sorted suffix array. + *

+ * @see LCP Array (Wikipedia) + *
+ * @author Jakub Szarawarski + * @author Justin Wetherell + */ +public class LCPArray { + + private static final char DEFAULT_END_SEQ_CHAR = '$'; + + private final char endSeqChar; + + private SuffixArray suffixArray; + private ArrayList lcp; + + public LCPArray(C sequence){ + this(sequence, DEFAULT_END_SEQ_CHAR); + } + + public LCPArray(C sequence, char endChar) { + endSeqChar = endChar; + suffixArray = new SuffixArray(sequence, endSeqChar); + } + + public ArrayList getLCPArray() { + if (lcp == null) + LCPAlgorithm(); + return lcp; + } + + private void LCPAlgorithm() { + final ArrayList LCPR = getLCPR(); + getLCPfromLCPR(LCPR); + } + + private ArrayList getLCPR() { + final ArrayList KMRArrayList = suffixArray.getKMRarray(); + final ArrayList suffixArrayList = suffixArray.getSuffixArray(); + final String string = suffixArray.getString(); + final int length = KMRArrayList.size(); + final ArrayList LCPR = new ArrayList(); // helper array, LCP[i] = LCPR[suffixArray[i]] + + int startingValue = 0; + for (int i=0; i 0 ? LCPRValue-1 : 0; + } + } + + return LCPR; + } + + private void getLCPfromLCPR(ArrayList LCPR) { + final ArrayList suffixArrayList = suffixArray.getSuffixArray(); + final int length = suffixArrayList.size(); + + lcp = new ArrayList(); + lcp.add(null); //no value for LCP[0] + for (int i=1; i + * @see Sequence (Wikipedia) + *
+ * @author Justin Wetherell + */ @SuppressWarnings("unchecked") -public interface List extends IList { +public abstract class List implements IList { /** * A dynamic array, growable array, resizable array, dynamic table, or array * list is a random access, variable-size list data structure that allows * elements to be added or removed. - * - * http://en.wikipedia.org/wiki/Dynamic_array - * + *

+ * @see Dynamic Array (Wikipedia) + *
* @author Justin Wetherell */ - public static class ArrayList implements List { + public static class ArrayList extends List { - private static final int MINIMUM_SIZE = 10; + private static final int MINIMUM_SIZE = 1024; private int size = 0; private T[] array = (T[]) new Object[MINIMUM_SIZE]; @@ -36,17 +46,16 @@ public boolean add(T value) { * @param value to add to list. */ public boolean add(int index, T value) { - int growSize = this.size; - if (size >= array.length) { - array = Arrays.copyOf(array, (growSize + (growSize>>1))); - } + if (size >= array.length) + grow(); if (index==size) { - array[size++] = value; + array[size] = value; } else { // Shift the array down one spot System.arraycopy(array, index, array, index+1, size - index); array[index] = value; } + size++; return true; } @@ -58,8 +67,8 @@ public boolean remove(T value) { for (int i = 0; i < size; i++) { T obj = array[i]; if (obj.equals(value)) { - if (remove(i)!=null) return true; - return false; + remove(i); + return true; } } return false; @@ -81,14 +90,25 @@ public T remove(int index) { } array[size] = null; - int shrinkSize = size; - if (size >= MINIMUM_SIZE && size < (shrinkSize + (shrinkSize<<1))) { - System.arraycopy(array, 0, array, 0, size); - } + int shrinkSize = array.length>>1; + if (shrinkSize >= MINIMUM_SIZE && size < shrinkSize) + shrink(); return t; } + // Grow the array by 50% + private void grow() { + int growSize = size + (size<<1); + array = Arrays.copyOf(array, growSize); + } + + // Shrink the array by 50% + private void shrink() { + int shrinkSize = array.length>>1; + array = Arrays.copyOf(array, shrinkSize); + } + /** * Set value at index. * @@ -189,15 +209,91 @@ public String toString() { } } + public static class JavaCompatibleArrayList extends java.util.AbstractList implements java.util.RandomAccess { + + private List.ArrayList list = null; + + public JavaCompatibleArrayList(List.ArrayList list) { + this.list = list; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean add(T value) { + return list.add(value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean remove(Object value) { + return list.remove((T)value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Object value) { + return list.contains((T)value); + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return list.size; + } + + /** + * {@inheritDoc} + */ + @Override + public void add(int index, T value) { + list.add(index, value); + } + + /** + * {@inheritDoc} + */ + @Override + public T remove(int index) { + return list.remove(index); + } + + /** + * {@inheritDoc} + */ + @Override + public T get(int index) { + T t = list.get(index); + if (t!=null) + return t; + throw new IndexOutOfBoundsException(); + } + + /** + * {@inheritDoc} + */ + @Override + public T set(int index, T value) { + return list.set(index, value); + } + } + /** - * Linked List (doubly link). A linked list is a data structure consisting + * Linked List (Singly link). A linked list is a data structure consisting * of a group of nodes which together represent a sequence. - * - * http://en.wikipedia.org/wiki/Linked_list - * + *

+ * @see Linked List (Wikipedia) + *
* @author Justin Wetherell */ - public static class LinkedList implements List { + public static class SinglyLinkedList extends List { private int size = 0; private Node head = null; @@ -224,7 +320,6 @@ private boolean add(Node node) { } else { Node prev = tail; prev.next = node; - node.prev = prev; tail = node; } size++; @@ -237,32 +332,36 @@ private boolean add(Node node) { @Override public boolean remove(T value) { // Find the node + Node prev = null; Node node = head; while (node != null && (!node.value.equals(value))) { + prev = node; node = node.next; } + if (node == null) return false; // Update the tail, if needed - if (node.equals(tail)) - tail = node.prev; + if (node.equals(tail)) { + tail = prev; + if (prev != null) + prev.next = null; + } - Node prev = node.prev; Node next = node.next; if (prev != null && next != null) { prev.next = next; - next.prev = prev; } else if (prev != null && next == null) { prev.next = null; } else if (prev == null && next != null) { // Node is the head - next.prev = null; head = next; } else { // prev==null && next==null head = null; } + size--; return true; } @@ -305,12 +404,13 @@ public int size() { public boolean validate() { java.util.Set keys = new java.util.HashSet(); Node node = head; - if (node!=null) { + if (node != null) { keys.add(node.value); - if (node.prev!=null) return false; + Node child = node.next; - while (child!=null) { - if (!validate(child,keys)) return false; + while (child != null) { + if (!validate(child,keys)) + return false; child = child.next; } } @@ -318,14 +418,15 @@ public boolean validate() { } private boolean validate(Node node, java.util.Set keys) { - if (node.value==null) return false; + if (node.value==null) + return false; + keys.add(node.value); Node child = node.next; - if (child!=null) { - if (!child.prev.equals(node)) return false; - } else { - if (!node.equals(tail)) return false; + if (child==null) { + if (!node.equals(tail)) + return false; } return true; } @@ -335,7 +436,7 @@ private boolean validate(Node node, java.util.Set keys) { */ @Override public java.util.List toList() { - return (new JavaCompatibleLinkedList(this)); + return (new JavaCompatibleSinglyLinkedList(this)); } /** @@ -343,7 +444,7 @@ public java.util.List toList() { */ @Override public java.util.Collection toCollection() { - return (new JavaCompatibleLinkedList(this)); + return (new JavaCompatibleSinglyLinkedList(this)); } /** @@ -363,7 +464,6 @@ public String toString() { private static class Node { private T value = null; - private Node prev = null; private Node next = null; private Node() { } @@ -377,17 +477,24 @@ private Node(T value) { */ @Override public String toString() { - return "value=" + value + " previous=" + ((prev != null) ? prev.value : "NULL") - + " next=" + ((next != null) ? next.value : "NULL"); + return "value=" + value + " next=" + ((next != null) ? next.value : "NULL"); } } } - public static class JavaCompatibleArrayList extends java.util.AbstractList implements java.util.RandomAccess { + /** + * Linked List (singly link). A linked list is a data structure consisting + * of a group of nodes which together represent a sequence. + *

+ * @see Linked List (Wikipedia) + *
+ * @author Justin Wetherell + */ + public static class JavaCompatibleSinglyLinkedList extends java.util.AbstractSequentialList { - private List.ArrayList list = null; + private List.SinglyLinkedList list = null; - public JavaCompatibleArrayList(List.ArrayList list) { + public JavaCompatibleSinglyLinkedList(List.SinglyLinkedList list) { this.list = list; } @@ -420,49 +527,377 @@ public boolean contains(Object value) { */ @Override public int size() { - return list.size; + return list.size(); } /** * {@inheritDoc} */ @Override - public void add(int index, T value) { - list.add(index, value); + public java.util.ListIterator listIterator(int index) { + return (new SinglyLinkedListListIterator(list)); + } + + private static class SinglyLinkedListListIterator implements java.util.ListIterator { + + private int index = 0; + + private SinglyLinkedList list = null; + private SinglyLinkedList.Node prev = null; + private SinglyLinkedList.Node next = null; + private SinglyLinkedList.Node last = null; + + private SinglyLinkedListListIterator(SinglyLinkedList list) { + this.list = list; + this.next = list.head; + this.prev = null; + this.last = null; + } + + /** + * {@inheritDoc} + */ + @Override + public void add(T value) { + SinglyLinkedList.Node node = new SinglyLinkedList.Node(value); + + if (list.head == null) { + list.head = node; + list.tail = node; + } else { + SinglyLinkedList.Node p = null; + SinglyLinkedList.Node n = list.head; + while (n!= null && !(n.equals(next))) { + p = node; + n = node.next; + } + if (p != null) { + p.next = node; + } else { + // replacing head + list.head = node; + } + node.next = n; + } + this.next = node; + + list.size++; + } + + /** + * {@inheritDoc} + */ + @Override + public void remove() { + if (last == null) + return; + + SinglyLinkedList.Node p = null; + SinglyLinkedList.Node node = this.last; + while (node!= null && !(node.equals(last))) { + p = node; + node = node.next; + } + + SinglyLinkedList.Node n = last.next; + if (p != null) + p.next = n; + + if (last.equals(list.head)) + list.head = n; + if (last.equals(list.tail)) + list.tail = p; + + list.size--; + } + + /** + * {@inheritDoc} + */ + @Override + public void set(T value) { + if (last != null) + last.value = value; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasNext() { + return (next!=null); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasPrevious() { + return (prev!=null); + } + + /** + * {@inheritDoc} + */ + @Override + public int nextIndex() { + return index; + } + + /** + * {@inheritDoc} + */ + @Override + public int previousIndex() { + return index-1; + } + + /** + * {@inheritDoc} + */ + @Override + public T next() { + if (next == null) + throw new java.util.NoSuchElementException(); + + index++; + last = next; + prev = next; + next = next.next; + + return last.value; + } + + /** + * {@inheritDoc} + */ + @Override + public T previous() { + if (prev == null) + throw new java.util.NoSuchElementException(); + + index--; + last = prev; + next = prev; + + SinglyLinkedList.Node p = null; + SinglyLinkedList.Node node = this.list.head; + while (node!= null && !(node.equals(prev))) { + p = node; + node = node.next; + } + prev = p; + + return last.value; + } } + } + + /** + * Linked List (doubly link). A linked list is a data structure consisting + * of a group of nodes which together represent a sequence. + *

+ * @see Linked List (Wikipedia) + *
+ * @author Justin Wetherell + */ + public static class DoublyLinkedList extends List { + + private int size = 0; + private Node head = null; + private Node tail = null; /** * {@inheritDoc} */ @Override - public T remove(int index) { - return list.remove(index); + public boolean add(T value) { + return add(new Node(value)); + } + + /** + * Add node to list. + * + * @param node + * to add to list. + */ + private boolean add(Node node) { + if (head == null) { + head = node; + tail = node; + } else { + Node prev = tail; + prev.next = node; + node.prev = prev; + tail = node; + } + size++; + return true; } /** * {@inheritDoc} */ @Override - public T get(int index) { - T t = list.get(index); - if (t!=null) return t; - throw new IndexOutOfBoundsException(); + public boolean remove(T value) { + // Find the node + Node node = head; + while (node != null && (!node.value.equals(value))) { + node = node.next; + } + if (node == null) + return false; + + // Update the tail, if needed + if (node.equals(tail)) + tail = node.prev; + + Node prev = node.prev; + Node next = node.next; + if (prev != null && next != null) { + prev.next = next; + next.prev = prev; + } else if (prev != null && next == null) { + prev.next = null; + } else if (prev == null && next != null) { + // Node is the head + next.prev = null; + head = next; + } else { + // prev==null && next==null + head = null; + } + size--; + return true; } /** * {@inheritDoc} */ @Override - public T set(int index, T value) { - return list.set(index, value); + public void clear() { + head = null; + size = 0; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(T value) { + Node node = head; + while (node != null) { + if (node.value.equals(value)) + return true; + node = node.next; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return size; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean validate() { + java.util.Set keys = new java.util.HashSet(); + Node node = head; + if (node!=null) { + keys.add(node.value); + if (node.prev!=null) + return false; + Node child = node.next; + while (child!=null) { + if (!validate(child,keys)) + return false; + child = child.next; + } + } + return (keys.size()==size); + } + + private boolean validate(Node node, java.util.Set keys) { + if (node.value==null) + return false; + + keys.add(node.value); + + Node child = node.next; + if (child!=null) { + if (!child.prev.equals(node)) + return false; + } else { + if (!node.equals(tail)) + return false; + } + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public java.util.List toList() { + return (new JavaCompatibleDoublyLinkedList(this)); + } + + /** + * {@inheritDoc} + */ + @Override + public java.util.Collection toCollection() { + return (new JavaCompatibleDoublyLinkedList(this)); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + Node node = head; + while (node != null) { + builder.append(node.value).append(", "); + node = node.next; + } + return builder.toString(); + } + + private static class Node { + + private T value = null; + private Node prev = null; + private Node next = null; + + private Node() { } + + private Node(T value) { + this.value = value; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "value=" + value + " previous=" + ((prev != null) ? prev.value : "NULL") + + " next=" + ((next != null) ? next.value : "NULL"); + } } } - public static class JavaCompatibleLinkedList extends java.util.AbstractSequentialList { + public static class JavaCompatibleDoublyLinkedList extends java.util.AbstractSequentialList { - private List.LinkedList list = null; + private List.DoublyLinkedList list = null; - public JavaCompatibleLinkedList(List.LinkedList list) { + public JavaCompatibleDoublyLinkedList(List.DoublyLinkedList list) { this.list = list; } @@ -503,22 +938,23 @@ public int size() { */ @Override public java.util.ListIterator listIterator(int index) { - return (new LinkedListListIterator(list)); + return (new DoublyLinkedListListIterator(list)); } - private static class LinkedListListIterator implements java.util.ListIterator { + private static class DoublyLinkedListListIterator implements java.util.ListIterator { private int index = 0; - private LinkedList list = null; - private LinkedList.Node prev = null; - private LinkedList.Node next = null; - private LinkedList.Node last = null; + private DoublyLinkedList list = null; + private DoublyLinkedList.Node prev = null; + private DoublyLinkedList.Node next = null; + private DoublyLinkedList.Node last = null; - private LinkedListListIterator(LinkedList list) { + private DoublyLinkedListListIterator(DoublyLinkedList list) { this.list = list; this.next = list.head; - if (this.next!=null) this.prev = next.prev; + this.prev = null; + this.last = null; } /** @@ -526,18 +962,20 @@ private LinkedListListIterator(LinkedList list) { */ @Override public void add(T value) { - LinkedList.Node node = new LinkedList.Node(value); - - LinkedList.Node n = this.next; + DoublyLinkedList.Node node = new DoublyLinkedList.Node(value); + DoublyLinkedList.Node n = this.next; - if (this.prev!=null) this.prev.next = node; + if (this.prev != null) + this.prev.next = node; node.prev = this.prev; node.next = n; - if (n!=null) n.prev = node; + if (n != null) + n.prev = node; this.next = node; - if (this.prev==null) list.head = node; // new root + if (this.prev == null) + list.head = node; // new root list.size++; } @@ -546,14 +984,20 @@ public void add(T value) { */ @Override public void remove() { - if (last==null) return; - - LinkedList.Node p = last.prev; - LinkedList.Node n = last.next; - if (p!=null) p.next = n; - if (n!=null) n.prev = p; - if (last.equals(list.head)) list.head = n; - if (last.equals(list.tail)) list.tail = p; + if (last == null) + return; + + DoublyLinkedList.Node p = last.prev; + DoublyLinkedList.Node n = last.next; + if (p != null) + p.next = n; + if (n != null) + n.prev = p; + if (last.equals(list.head)) + list.head = n; + if (last.equals(list.tail)) + list.tail = p; + list.size--; } @@ -562,7 +1006,8 @@ public void remove() { */ @Override public void set(T value) { - if (last!=null) last.value = value; + if (last != null) + last.value = value; } /** @@ -602,7 +1047,9 @@ public int previousIndex() { */ @Override public T next() { - if (next == null) throw new java.util.NoSuchElementException(); + if (next == null) + throw new java.util.NoSuchElementException(); + index++; last = next; prev = next; @@ -616,7 +1063,9 @@ public T next() { */ @Override public T previous() { - if (prev == null) throw new java.util.NoSuchElementException(); + if (prev == null) + throw new java.util.NoSuchElementException(); + index--; last = prev; next = prev; diff --git a/src/com/jwetherell/algorithms/data_structures/LowestCommonAncestor.java b/src/com/jwetherell/algorithms/data_structures/LowestCommonAncestor.java new file mode 100644 index 00000000..5d9f0bca --- /dev/null +++ b/src/com/jwetherell/algorithms/data_structures/LowestCommonAncestor.java @@ -0,0 +1,175 @@ +package com.jwetherell.algorithms.data_structures; + +import java.util.ArrayList; +import java.util.List; + +/** + * Structure for storing rooted tree which allows to find lowest common ancestor. + *

+ * @param type of value stored in nodes. + *
+ * @author Szymon Stankiewicz + * @author Justin Wetherell + */ +public class LowestCommonAncestor { + + /** + * Exception which can be thrown by lowestCommonAncestor function if two + * nodes are in different trees. + * + */ + public static class NodesNotInSameTreeException extends Exception { + private static final long serialVersionUID = -5366787886097250564L; + } + + /** + * Finds lower common ancestor of two nodes. + * + * Complexity O(log n) where n is the height of the tree. + * + * @param node1 first node + * @param node2 second node + * @return lower common ancestor + * @throws NodesNotInSameTreeException if nodes don't have common root + */ + public static TreeNode lowestCommonAncestor(TreeNode node1, TreeNode node2) throws NodesNotInSameTreeException { + if (node1 == node2) + return node1; + else if (node1.depth < node2.depth) + return lowestCommonAncestor(node2, node1); + else if (node1.depth > node2.depth) { + int diff = node1.depth - node2.depth; + int jump = 0; + while (diff > 0) { + if (diff % 2 == 1) + node1 = node1.ancestors.get(jump); + jump++; + diff /= 2; + } + return lowestCommonAncestor(node1, node2); + } else { + try { + int step = 0; + while (1<<(step+1) <= node1.depth) + step++; + while (step >= 0) { + if(step < node1.ancestors.size() && node1.ancestors.get(step) != node2.ancestors.get(step)) { + node1 = node1.ancestors.get(step); + node2 = node2.ancestors.get(step); + } + step--; + } + return node1.ancestors.get(0); + } catch (Exception e) { + throw new NodesNotInSameTreeException(); + } + + } + } + + public static final class TreeNode { + + private final List> ancestors = new ArrayList>(); + private final List> children = new ArrayList>(); + + private T value = null; + private int depth = 0; + + /** + * Creates tree with root only. + * + */ + public TreeNode() { } + + /** + * Creates tree with root (storing value) only. + * + * @param value value to be stored in root + */ + public TreeNode(T value) { + this.value = value; + } + + private TreeNode(TreeNode parent) { + parent.children.add(this); + this.ancestors.add(parent); + this.depth = parent.depth + 1; + int dist = 0; + while (true) { + try { + this.ancestors.add(this.ancestors.get(dist).ancestors.get(dist)); + dist++; + } catch (Exception e){ + break; + } + } + } + + public TreeNode setValue(T value) { + this.value = value; + return this; + } + + /** + * Creates new child for this node and returns it. + * + * Complexity O(log depth) + * + * @return added child + */ + public TreeNode addChild() { + return new TreeNode(this); + } + + /** + * Creates new child (storing value) for this node and returns it. + * + * Complexity O(log depth) + * + * @param value value to be stored in new child + * @return added child + */ + public TreeNode addChild(T value) { + return addChild().setValue(value); + } + + /** + * Returns value stored in node. + * + * @return node's value. + */ + public T getValue() { + return value; + } + + /** + * Finds subtree with given value in the root. + * + * @param value value to be find + * @return subtree with given value in the root + */ + public TreeNode find(T value) { + if (this.value == null) { + if (value == null) + return this; + } else if (this.value.equals(value)) + return this; + for (TreeNode child: children) { + final TreeNode res = child.find(value); + if (res != null) + return res; + } + return null; + } + + /** + * Returns true if tree contains a node with given value + * + * @param value to be checked + * @return true if tree contains node with given value, false otherwise + */ + public boolean contains(T value) { + return find(value) != null; + } + } +} diff --git a/src/com/jwetherell/algorithms/data_structures/Matrix.java b/src/com/jwetherell/algorithms/data_structures/Matrix.java index 781d2991..0d814e18 100644 --- a/src/com/jwetherell/algorithms/data_structures/Matrix.java +++ b/src/com/jwetherell/algorithms/data_structures/Matrix.java @@ -1,26 +1,90 @@ package com.jwetherell.algorithms.data_structures; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Comparator; + /** - * Matrx. This Matrix is designed to be more efficient in cache. A matrix is a - * rectangular array of numbers, symbols, or expressions. - * - * http://en.wikipedia.org/wiki/Matrix_(mathematics) - * + * Matrx. This Matrix implementation is designed to be more efficient + * in cache. A matrix is a rectangular array of numbers, symbols, or expressions. + *

+ * @see Matrix (Wikipedia) + *
* @author Justin Wetherell */ +@SuppressWarnings("unchecked") public class Matrix { private int rows = 0; private int cols = 0; private T[] matrix = null; - @SuppressWarnings("unchecked") + private final Comparator comparator = new Comparator() { + /** + * {@inheritDoc} + */ + @Override + public int compare(T o1, T o2) { + /* TODO: What if Java adds new numeric type? */ + int result = 0; + if (o1 instanceof BigDecimal || o2 instanceof BigDecimal) { + BigDecimal c1 = (BigDecimal)o1; + BigDecimal c2 = (BigDecimal)o2; + result = c1.compareTo(c2); + } else if (o1 instanceof BigInteger || o2 instanceof BigInteger) { + BigInteger c1 = (BigInteger)o1; + BigInteger c2 = (BigInteger)o2; + result = c1.compareTo(c2); + } else if (o1 instanceof Long || o2 instanceof Long) { + Long c1 = o1.longValue(); + Long c2 = o2.longValue(); + result = c1.compareTo(c2); + } else if (o1 instanceof Double || o2 instanceof Double) { + Double c1 = o1.doubleValue(); + Double c2 = o2.doubleValue(); + result = c1.compareTo(c2); + } else if (o1 instanceof Float || o2 instanceof Float) { + Float c1 = o1.floatValue(); + Float c2 = o2.floatValue(); + result = c1.compareTo(c2); + } else { + Integer c1 = o1.intValue(); + Integer c2 = o2.intValue(); + result = c1.compareTo(c2); + } + return result; + } + }; + + /** + * Matrix with 'rows' number of rows and 'cols' number of columns. + * + * @param rows Number of rows in Matrix. + * @param cols Number of columns in Matrix. + */ public Matrix(int rows, int cols) { this.rows = rows; this.cols = cols; this.matrix = (T[]) new Number[rows * cols]; } + /** + * Matrix with 'rows' number of rows and 'cols' number of columns, populates + * the double index matrix. + * + * @param rows Number of rows in Matrix. + * @param cols Number of columns in Matrix. + * @param matrix 2D matrix used to populate Matrix. + */ + public Matrix(int rows, int cols, T[][] matrix) { + this.rows = rows; + this.cols = cols; + this.matrix = (T[]) new Number[rows * cols]; + for (int r=0; r identity() throws Exception{ + if(this.rows != this.cols) + throw new Exception("Matrix should be a square"); + + final T element = this.get(0, 0); + final T zero; + final T one; + if (element instanceof BigDecimal) { + zero = (T)BigDecimal.ZERO; + one = (T)BigDecimal.ONE; + } else if(element instanceof BigInteger){ + zero = (T)BigInteger.ZERO; + one = (T)BigInteger.ONE; + } else if(element instanceof Long){ + zero = (T)new Long(0); + one = (T)new Long(1); + } else if(element instanceof Double){ + zero = (T)new Double(0); + one = (T)new Double(1); + } else if(element instanceof Float){ + zero = (T)new Float(0); + one = (T)new Float(1); + } else { + zero = (T)new Integer(0); + one = (T)new Integer(1); + } + + final T array[][] = (T[][])new Number[this.rows][this.cols]; + for(int i = 0; i < this.rows; ++i) { + for(int j = 0 ; j < this.cols; ++j){ + array[i][j] = zero; + } + } + + final Matrix identityMatrix = new Matrix(this.rows, this.cols, array); + for(int i = 0; i < this.rows;++i){ + identityMatrix.set(i, i, one); + } + return identityMatrix; + } + public Matrix add(Matrix input) { Matrix output = new Matrix(this.rows, this.cols); if ((this.cols != input.cols) || (this.rows != input.rows)) return output; - for (int r = 0; r < output.rows; r++) { for (int c = 0; c < output.cols; c++) { for (int i = 0; i < cols; i++) { T m1 = this.get(r, c); T m2 = input.get(r, c); - Long l = m1.longValue() + m2.longValue(); - output.set(r, c, (T) l); + T result; + /* TODO: This is ugly and how to handle number overflow? */ + if (m1 instanceof BigDecimal || m2 instanceof BigDecimal) { + BigDecimal result2 = ((BigDecimal)m1).add((BigDecimal)m2); + result = (T)result2; + } else if (m1 instanceof BigInteger || m2 instanceof BigInteger) { + BigInteger result2 = ((BigInteger)m1).add((BigInteger)m2); + result = (T)result2; + } else if (m1 instanceof Long || m2 instanceof Long) { + Long result2 = (m1.longValue() + m2.longValue()); + result = (T)result2; + } else if (m1 instanceof Double || m2 instanceof Double) { + Double result2 = (m1.doubleValue() + m2.doubleValue()); + result = (T)result2; + } else if (m1 instanceof Float || m2 instanceof Float) { + Float result2 = (m1.floatValue() + m2.floatValue()); + result = (T)result2; + } else { + // Integer + Integer result2 = (m1.intValue() + m2.intValue()); + result = (T)result2; + } + output.set(r, c, result); } } } return output; } - @SuppressWarnings("unchecked") public Matrix subtract(Matrix input) { Matrix output = new Matrix(this.rows, this.cols); if ((this.cols != input.cols) || (this.rows != input.rows)) @@ -83,15 +204,35 @@ public Matrix subtract(Matrix input) { for (int i = 0; i < cols; i++) { T m1 = this.get(r, c); T m2 = input.get(r, c); - Long l = m1.longValue() - m2.longValue(); - output.set(r, c, (T) l); + T result; + /* TODO: This is ugly and how to handle number overflow? */ + if (m1 instanceof BigDecimal || m2 instanceof BigDecimal) { + BigDecimal result2 = ((BigDecimal)m1).subtract((BigDecimal)m2); + result = (T)result2; + } else if (m1 instanceof BigInteger || m2 instanceof BigInteger) { + BigInteger result2 = ((BigInteger)m1).subtract((BigInteger)m2); + result = (T)result2; + } else if (m1 instanceof Long || m2 instanceof Long) { + Long result2 = (m1.longValue() - m2.longValue()); + result = (T)result2; + } else if (m1 instanceof Double || m2 instanceof Double) { + Double result2 = (m1.doubleValue() - m2.doubleValue()); + result = (T)result2; + } else if (m1 instanceof Float || m2 instanceof Float) { + Float result2 = (m1.floatValue() - m2.floatValue()); + result = (T)result2; + } else { + // Integer + Integer result2 = (m1.intValue() - m2.intValue()); + result = (T)result2; + } + output.set(r, c, result); } } } return output; } - @SuppressWarnings("unchecked") public Matrix multiply(Matrix input) { Matrix output = new Matrix(this.rows, input.cols); if (this.cols != input.rows) @@ -101,12 +242,70 @@ public Matrix multiply(Matrix input) { for (int c = 0; c < output.cols; c++) { T[] row = getRow(r); T[] column = input.getColumn(c); - Long result = 0l; - for (int i = 0; i < cols; i++) { - Long l = row[i].longValue() * column[i].longValue(); - result += l; + T test = row[0]; + /* TODO: This is ugly and how to handle number overflow? */ + if (test instanceof BigDecimal) { + BigDecimal result = BigDecimal.ZERO; + for (int i = 0; i < cols; i++) { + T m1 = row[i]; + T m2 = column[i]; + + BigDecimal result2 = ((BigDecimal)m1).multiply(((BigDecimal)m2)); + result = result.add(result2); + } + output.set(r, c, (T)result); + } else if (test instanceof BigInteger) { + BigInteger result = BigInteger.ZERO; + for (int i = 0; i < cols; i++) { + T m1 = row[i]; + T m2 = column[i]; + + BigInteger result2 = ((BigInteger)m1).multiply(((BigInteger)m2)); + result = result.add(result2); + } + output.set(r, c, (T)result); + } else if (test instanceof Long) { + Long result = 0l; + for (int i = 0; i < cols; i++) { + T m1 = row[i]; + T m2 = column[i]; + + Long result2 = m1.longValue() * m2.longValue(); + result = result+result2; + } + output.set(r, c, (T)result); + } else if (test instanceof Double) { + Double result = 0d; + for (int i = 0; i < cols; i++) { + T m1 = row[i]; + T m2 = column[i]; + + Double result2 = m1.doubleValue() * m2.doubleValue(); + result = result+result2; + } + output.set(r, c, (T)result); + } else if (test instanceof Float) { + Float result = 0f; + for (int i = 0; i < cols; i++) { + T m1 = row[i]; + T m2 = column[i]; + + Float result2 = m1.floatValue() * m2.floatValue(); + result = result+result2; + } + output.set(r, c, (T)result); + } else { + // Integer + Integer result = 0; + for (int i = 0; i < cols; i++) { + T m1 = row[i]; + T m2 = column[i]; + + Integer result2 = m1.intValue() * m2.intValue(); + result = result+result2; + } + output.set(r, c, (T)result); } - output.set(r, c, (T) result); } } return output; @@ -120,6 +319,42 @@ public void copy(Matrix m) { } } + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int hash = this.rows + this.cols; + for (T t : matrix) + hash += t.intValue(); + return 31 * hash; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (!(obj instanceof Matrix)) + return false; + + Matrix m = (Matrix) obj; + if (this.rows != m.rows) + return false; + if (this.cols != m.cols) + return false; + for (int i=0; i + * @see Radix Tree / Patricia Trie (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") @@ -22,7 +24,17 @@ public class PatriciaTrie implements ITree { protected static final boolean BLACK = false; // non-terminating protected static final boolean WHITE = true; // terminating - public PatriciaTrie() { } + public PatriciaTrie() { + this.creator = new INodeCreator() { + /** + * {@inheritDoc} + */ + @Override + public Node createNewNode(Node parent, char[] seq, boolean type) { + return (new Node(parent, seq, type)); + } + }; + } /** * Constructor with external Node creator. @@ -31,21 +43,6 @@ public PatriciaTrie(INodeCreator creator) { this.creator = creator; } - /** - * Create a new node for sequence. - * - * @param parent - * node of the new node. - * @param seq - * of characters which represents this node. - * @param type - * of the node, can be either BLACK or WHITE. - * @return Node which was created. - */ - protected Node createNewNode(Node parent, char[] seq, boolean type) { - return (new Node(parent, seq, type)); - } - /** * {@inheritDoc} */ @@ -65,12 +62,8 @@ public boolean add(C seq) { * sequence already exists. */ protected Node addSequence(C seq) { - if (root == null) { - if (this.creator == null) - root = createNewNode(null, null, BLACK); - else - root = this.creator.createNewNode(null, null, BLACK); - } + if (root == null) + root = this.creator.createNewNode(null, null, BLACK); int indexIntoParent = -1; int indexIntoString = -1; @@ -104,22 +97,19 @@ protected Node addSequence(C seq) { } Node addedNode = null; + Node parent = node.parent; if (node.string != null && indexIntoParent < node.string.length) { char[] parentString = Arrays.copyOfRange(node.string, 0, indexIntoParent); char[] refactorString = Arrays.copyOfRange(node.string, indexIntoParent, node.string.length); - Node parent = node.parent; if (indexIntoString < seq.length()) { - // Creating a new parent by splitting a previous node and adding - // a new node + // Creating a new parent by splitting a previous node and adding a new node // Create new parent - if (parent != null) parent.removeChild(node); - Node newParent = null; - if (this.creator == null) - newParent = createNewNode(parent, parentString, BLACK); - else - newParent = this.creator.createNewNode(parent, parentString, BLACK); + if (parent != null) + parent.removeChild(node); + + Node newParent = this.creator.createNewNode(parent, parentString, BLACK); if (parent != null) parent.addChild(newParent); @@ -131,25 +121,17 @@ protected Node addSequence(C seq) { // Create a new node from the rest of the string CharSequence newString = seq.subSequence(indexIntoString, seq.length()); - Node newNode2 = null; - if (this.creator == null) - newNode2 = createNewNode(newParent, newString.toString().toCharArray(), WHITE); - else - newNode2 = this.creator.createNewNode(newParent, newString.toString().toCharArray(), WHITE); + Node newNode2 = this.creator.createNewNode(newParent, newString.toString().toCharArray(), WHITE); newParent.addChild(newNode2); // New node which was added addedNode = newNode2; } else { - // Creating a new parent by splitting a previous node and - // converting the previous node + // Creating a new parent by splitting a previous node and converting the previous node if (parent != null) parent.removeChild(node); - Node newParent = null; - if (this.creator == null) - newParent = createNewNode(parent, parentString, WHITE); - else - newParent = this.creator.createNewNode(parent, parentString, WHITE); + + Node newParent = this.creator.createNewNode(parent, parentString, WHITE); if (parent != null) parent.addChild(newParent); @@ -175,20 +157,12 @@ protected Node addSequence(C seq) { } else if (node.string != null) { // Adding a child CharSequence newString = seq.subSequence(indexIntoString, seq.length()); - Node newNode = null; - if (this.creator == null) - newNode = createNewNode(node, newString.toString().toCharArray(), WHITE); - else - newNode = this.creator.createNewNode(node, newString.toString().toCharArray(), WHITE); + Node newNode = this.creator.createNewNode(node, newString.toString().toCharArray(), WHITE); node.addChild(newNode); addedNode = newNode; } else { // Add to root node - Node newNode = null; - if (this.creator == null) - newNode = createNewNode(node, seq.toString().toCharArray(), WHITE); - else - newNode = this.creator.createNewNode(node, seq.toString().toCharArray(), WHITE); + Node newNode = this.creator.createNewNode(node, seq.toString().toCharArray(), WHITE); node.addChild(newNode); addedNode = newNode; } @@ -220,7 +194,8 @@ public C remove(C seq) { } protected void remove(Node node) { - if (node == null) return; + if (node == null) + return; // No longer a white node (leaf) node.type = BLACK; @@ -228,7 +203,8 @@ protected void remove(Node node) { Node parent = node.parent; if (node.getChildrenSize() == 0) { // Remove the node if it has no children - if (parent != null) parent.removeChild(node); + if (parent != null) + parent.removeChild(node); } else if (node.getChildrenSize() == 1) { // Merge the node with it's child and add to node's parent Node child = node.getChild(0); @@ -249,7 +225,8 @@ protected void remove(Node node) { Node child = parent.getChild(0); // Merge with parent StringBuilder builder = new StringBuilder(); - if (parent.string != null) builder.append(parent.string); + if (parent.string != null) + builder.append(parent.string); builder.append(child.string); child.string = builder.toString().toCharArray(); if (parent.parent != null) { @@ -307,14 +284,17 @@ protected Node getNode(C seq) { } } - if (node.string != null && indexIntoParent == (node.string.length - 1)) { + if (node.string!=null && indexIntoParent == (node.string.length - 1)) { // Get the partial string to compare against the node's string int length = node.string.length; CharSequence sub = seq.subSequence(seq.length() - length, seq.length()); for (int i = 0; i < length; i++) { - if (node.string[i] != sub.charAt(i)) return null; + if (node.string[i] != sub.charAt(i)) + return null; } - return node; + + if (node.type==WHITE) + return node; } return null; } @@ -334,26 +314,33 @@ public int size() { public boolean validate() { java.util.Set keys = new java.util.HashSet(); Node node = root; - if (node!=null && !validate(node,"",keys)) return false; + if (node!=null && !validate(node,"",keys)) + return false; return (keys.size()==size()); } private boolean validate(Node node, String string, java.util.Set keys) { StringBuilder builder = new StringBuilder(string); - if (node.string!=null) builder.append(node.string); + if (node.string!=null) + builder.append(node.string); String s = builder.toString(); if (node.type == WHITE) { C c = (C)s; - if (c==null) return false; - if (keys.contains(c)) return false; + if (c==null) + return false; + if (keys.contains(c)) + return false; keys.add(c); } for (int i=0; i 0 && c.string[0] == character) return i; + if (c.string != null && c.string.length > 0 && c.string[0] == character) + return i; } return Integer.MIN_VALUE; } protected boolean removeChild(int index) { - if (index >= childrenSize) return false; + if (index >= childrenSize) + return false; children[index] = null; childrenSize--; @@ -443,7 +432,8 @@ protected boolean removeChild(int index) { } protected Node getChild(int index) { - if (index >= childrenSize) return null; + if (index >= childrenSize) + return null; return children[index]; } @@ -453,7 +443,8 @@ protected int getChildrenSize() { protected boolean partOfThis(char c, int idx) { // Search myself - if (string != null && idx < string.length && string[idx] == c) return true; + if (string != null && idx < string.length && string[idx] == c) + return true; return false; } @@ -462,7 +453,7 @@ protected Node getChildBeginningWithChar(char c) { Node node = null; for (int i = 0; i < this.childrenSize; i++) { Node child = this.children[i]; - if (child.string[0] == c) + if (child.string.length>0 && child.string[0] == c) return child; } return node; @@ -475,7 +466,8 @@ protected Node getChildBeginningWithChar(char c) { public String toString() { StringBuilder builder = new StringBuilder(); String output = null; - if (string != null) output = String.valueOf(string); + if (string != null) + output = String.valueOf(string); builder.append("string = ").append((output != null) ? output : "NULL").append("\n"); builder.append("type = ").append(type).append("\n"); return builder.toString(); @@ -486,7 +478,8 @@ public String toString() { */ @Override public int compareTo(Node node) { - if (node == null) return -1; + if (node == null) + return -1; int length = string.length; if (node.string.length < length) length = node.string.length; @@ -494,14 +487,19 @@ public int compareTo(Node node) { Character a = string[i]; Character b = node.string[i]; int c = a.compareTo(b); - if (c != 0) return c; + if (c != 0) + return c; } - if (this.type == BLACK && node.type == WHITE) return -1; - else if (node.type == BLACK && this.type == WHITE) return 1; + if (this.type == BLACK && node.type == WHITE) + return -1; + else if (node.type == BLACK && this.type == WHITE) + return 1; - if (this.getChildrenSize() < node.getChildrenSize()) return -1; - else if (this.getChildrenSize() > node.getChildrenSize()) return 1; + if (this.getChildrenSize() < node.getChildrenSize()) + return -1; + else if (this.getChildrenSize() > node.getChildrenSize()) + return 1; return 0; } @@ -523,29 +521,33 @@ protected static interface INodeCreator { public Node createNewNode(Node parent, char[] seq, boolean type); } - protected static class PatriciaTriePrinter { + protected static class PatriciaTriePrinter { protected static String getString(PatriciaTrie tree) { + if (tree.root == null) + return "Tree has no nodes."; return getString(tree.root, "", null, true); } - protected static String getString(Node node, String prefix, String previousString, boolean isTail) { + protected static String getString(Node node, String prefix, String previousString, boolean isTail) { StringBuilder builder = new StringBuilder(); - String thisString = null; - if (node.string != null) thisString = String.valueOf(node.string); + String thisString = ""; + if (node.string != null) + thisString = String.valueOf(node.string); String fullString = ((previousString != null) ? previousString : "") + thisString; builder.append(prefix + (isTail ? "└── " : "├── ") - + ((thisString != null) ? "[" + ((node.type == WHITE) ? "white" : "black") + "] " - + ((node.type == WHITE) ? "(" + thisString + ") " + fullString : thisString) : "[" - + ((node.type == WHITE) ? "white" : "black") + "]") + "\n"); + + ((thisString != null) ? + "[" + ((node.type == WHITE) ? "white" : "black") + "] " + + ((node.type == WHITE) ? "(" + thisString + ") " + fullString : thisString) + : "[" + + ((node.type == WHITE) ? "white" : "black") + "]") + "\n"); if (node.children != null) { for (int i = 0; i < node.getChildrenSize() - 1; i++) { - builder.append(getString(node.getChild(i), prefix + (isTail ? " " : "│ "), thisString, false)); + builder.append(getString(node.getChild(i), prefix + (isTail ? " " : "│ "), fullString, false)); } if (node.getChildrenSize() >= 1) { - builder.append(getString(node.getChild(node.getChildrenSize() - 1), prefix - + (isTail ? " " : "│ "), thisString, true)); + builder.append(getString(node.getChild(node.getChildrenSize() - 1), prefix + (isTail ? " " : "│ "), fullString, true)); } } return builder.toString(); @@ -611,16 +613,17 @@ private static class PatriciaTrieIterator implements jav protected PatriciaTrieIterator(PatriciaTrie trie) { this.trie = trie; java.util.Map map = new java.util.LinkedHashMap(); - if (this.trie.root!=null) { + if (this.trie.root!=null) getNodesWhichRepresentsWords(this.trie.root,"",map); - } iterator = map.entrySet().iterator(); } private void getNodesWhichRepresentsWords(PatriciaTrie.Node node, String string, java.util.Map nodesMap) { StringBuilder builder = new StringBuilder(string); - if (node.string!=null) builder.append(node.string); - if (node.type == PatriciaTrie.WHITE) nodesMap.put(node,builder.toString()); //Terminating + if (node.string!=null) + builder.append(node.string); + if (node.type == PatriciaTrie.WHITE) + nodesMap.put(node,builder.toString()); //Terminating for (int i=0; i entry = iterator.next(); lastNode = entry.getKey(); @@ -653,7 +658,8 @@ public C next() { */ @Override public void remove() { - if (iterator==null || trie==null) return; + if (iterator==null || trie==null) + return; iterator.remove(); this.trie.remove(lastNode); diff --git a/src/com/jwetherell/algorithms/data_structures/QuadTree.java b/src/com/jwetherell/algorithms/data_structures/QuadTree.java index bbc807dc..0535a245 100644 --- a/src/com/jwetherell/algorithms/data_structures/QuadTree.java +++ b/src/com/jwetherell/algorithms/data_structures/QuadTree.java @@ -1,6 +1,8 @@ package com.jwetherell.algorithms.data_structures; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; @@ -9,12 +11,13 @@ * A quadtree is a tree data structure in which each internal node has exactly four children. Quadtrees * are most often used to partition a two dimensional space by recursively subdividing it into four * quadrants or regions. The regions may be square or rectangular, or may have arbitrary shapes. - * - * http://en.wikipedia.org/wiki/Quadtree - * + *

+ * @see QuadTree (Wikipedia) + *
* @author Justin Wetherell */ -public abstract class QuadTree { +@SuppressWarnings("unchecked") +public abstract class QuadTree { /** * Get the root node. @@ -26,7 +29,23 @@ public abstract class QuadTree { /** * Range query of the quadtree. */ - public abstract List queryRange(float x, float y, float width, float height); + public abstract Collection queryRange(double x, double y, double width, double height); + + /** + * Insert point at X,Y into tree. + * + * @param x X position of point. + * @param y Y position of point. + */ + public abstract boolean insert(double x, double y); + + /** + * Remove point at X,Y from tree. + * + * @param x X position of point. + * @param y Y position of point. + */ + public abstract boolean remove(double x, double y); /** * {@inheritDoc} @@ -45,6 +64,9 @@ public String toString() { */ public static class PointRegionQuadTree

extends QuadTree

{ + private static final XYPoint XY_POINT = new XYPoint(); + private static final AxisAlignedBoundingBox RANGE = new AxisAlignedBoundingBox(); + private PointRegionQuadNode

root = null; /** @@ -56,7 +78,7 @@ public static class PointRegionQuadTree

extends Quad * @param width Width of the bounding box containing all points * @param height Height of the bounding box containing all points */ - public PointRegionQuadTree(float x, float y, float width, float height) { + public PointRegionQuadTree(double x, double y, double width, double height) { this(x,y,width,height,4,20); } @@ -70,7 +92,7 @@ public PointRegionQuadTree(float x, float y, float width, float height) { * @param height Height of the bounding box containing all points * @param leafCapacity Max capacity of leaf nodes. (Note: All data is stored in leaf nodes) */ - public PointRegionQuadTree(float x, float y, float width, float height, int leafCapacity) { + public PointRegionQuadTree(double x, double y, double width, double height, int leafCapacity) { this(x,y,width,height,leafCapacity,20); } @@ -86,7 +108,7 @@ public PointRegionQuadTree(float x, float y, float width, float height, int leaf * @param maxTreeHeight Max height of the quadtree. (Note: If this is defined, the tree will ignore the * max capacity defined by leafCapacity) */ - public PointRegionQuadTree(float x, float y, float width, float height, int leafCapacity, int maxTreeHeight) { + public PointRegionQuadTree(double x, double y, double width, double height, int leafCapacity, int maxTreeHeight) { XYPoint xyPoint = new XYPoint(x,y); AxisAlignedBoundingBox aabb = new AxisAlignedBoundingBox(xyPoint,width,height); PointRegionQuadNode.maxCapacity = leafCapacity; @@ -103,39 +125,38 @@ public QuadTree.QuadNode

getRoot() { } /** - * Insert point at X,Y into tree. - * - * @param x X position of point. - * @param y Y position of point. + * {@inheritDoc} */ - @SuppressWarnings("unchecked") - public boolean insert(float x, float y) { + @Override + public boolean insert(double x, double y) { XYPoint xyPoint = new XYPoint(x,y); + return root.insert((P)xyPoint); } /** - * Remove point at X,Y from tree. - * - * @param x X position of point. - * @param y Y position of point. + * {@inheritDoc} */ - @SuppressWarnings("unchecked") - public boolean remove(float x, float y) { - XYPoint xyPoint = new XYPoint(x,y); - return root.remove((P)xyPoint); + @Override + public boolean remove(double x, double y) { + XY_POINT.set(x,y); + + return root.remove((P)XY_POINT); } /** * {@inheritDoc} */ @Override - public List

queryRange(float x, float y, float width, float height) { + public Collection

queryRange(double x, double y, double width, double height) { + if (root == null) + return Collections.EMPTY_LIST; + + XY_POINT.set(x,y); + RANGE.set(XY_POINT,width,height); + List

pointsInRange = new LinkedList

(); - if (root==null) return pointsInRange; - XYPoint xyPoint = new XYPoint(x,y); - AxisAlignedBoundingBox range = new AxisAlignedBoundingBox(xyPoint,width,height); - root.queryRange(range,pointsInRange); + root.queryRange(RANGE,pointsInRange); return pointsInRange; } @@ -162,7 +183,8 @@ protected PointRegionQuadNode(AxisAlignedBoundingBox aabb) { @Override protected boolean insert(XY p) { // Ignore objects which do not belong in this quad tree - if (!aabb.containsPoint(p) || (isLeaf() && points.contains(p))) return false; // object cannot be added + if (!aabb.containsPoint(p) || (isLeaf() && points.contains(p))) + return false; // object cannot be added // If there is space in this quad tree, add the object here if ((height==maxHeight) || (isLeaf() && points.size() < maxCapacity)) { @@ -171,11 +193,9 @@ protected boolean insert(XY p) { } // Otherwise, we need to subdivide then add the point to whichever node will accept it - if (isLeaf() && height(aabbNW); ((PointRegionQuadNode)northWest).height = height+1; - XYPoint xyNE = new XYPoint(aabb.upperLeft.x+w,aabb.upperLeft.y); + XYPoint xyNE = new XYPoint(aabb.x+w,aabb.y); AxisAlignedBoundingBox aabbNE = new AxisAlignedBoundingBox(xyNE,w,h); northEast = new PointRegionQuadNode(aabbNE); ((PointRegionQuadNode)northEast).height = height+1; - XYPoint xySW = new XYPoint(aabb.upperLeft.x,aabb.upperLeft.y+h); + XYPoint xySW = new XYPoint(aabb.x,aabb.y+h); AxisAlignedBoundingBox aabbSW = new AxisAlignedBoundingBox(xySW,w,h); southWest = new PointRegionQuadNode(aabbSW); ((PointRegionQuadNode)southWest).height = height+1; - XYPoint xySE = new XYPoint(aabb.upperLeft.x+w,aabb.upperLeft.y+h); + XYPoint xySE = new XYPoint(aabb.x+w,aabb.y+h); AxisAlignedBoundingBox aabbSE = new AxisAlignedBoundingBox(xySE,w,h); southEast = new PointRegionQuadNode(aabbSE); ((PointRegionQuadNode)southEast).height = height+1; // points live in leaf nodes, so distribute - for (XY p : points) { + for (XY p : points) insertIntoChildren(p); - } points.clear(); } private void merge() { // If the children aren't leafs, you cannot merge - if (!northWest.isLeaf() || !northEast.isLeaf() || !southWest.isLeaf() || !southEast.isLeaf()) return; + if (!northWest.isLeaf() || !northEast.isLeaf() || !southWest.isLeaf() || !southEast.isLeaf()) + return; // Children and leafs, see if you can remove point and merge into this node int nw = northWest.size(); @@ -293,12 +316,14 @@ private boolean removeFromChildren(XY p) { @Override protected void queryRange(AxisAlignedBoundingBox range, List pointsInRange) { // Automatically abort if the range does not collide with this quad - if (!aabb.intersectsBox(range)) return; + if (!aabb.intersectsBox(range)) + return; // If leaf, check objects at this level if (isLeaf()) { for (XY xyPoint : points) { - if (range.containsPoint(xyPoint)) pointsInRange.add(xyPoint); + if (range.containsPoint(xyPoint)) + pointsInRange.add(xyPoint); } return; } @@ -333,6 +358,9 @@ public String toString() { */ public static class MxCifQuadTree extends QuadTree { + private static final XYPoint XY_POINT = new XYPoint(); + private static final AxisAlignedBoundingBox RANGE = new AxisAlignedBoundingBox(); + private MxCifQuadNode root = null; /** @@ -344,7 +372,7 @@ public static class MxCifQuadTree ext * @param width Width of the bounding box containing all points * @param height Height of the bounding box containing all points */ - public MxCifQuadTree(float x, float y, float width, float height) { + public MxCifQuadTree(double x, double y, double width, double height) { this(x,y,width,height,0,0); } @@ -359,7 +387,7 @@ public MxCifQuadTree(float x, float y, float width, float height) { * @param minWidth The tree will stop splitting when leaf node's width <= minWidth * @param minHeight The tree will stop splitting when leaf node's height <= minHeight */ - public MxCifQuadTree(float x, float y, float width, float height, float minWidth, float minHeight) { + public MxCifQuadTree(double x, double y, double width, double height, double minWidth, double minHeight) { XYPoint xyPoint = new XYPoint(x,y); AxisAlignedBoundingBox aabb = new AxisAlignedBoundingBox(xyPoint,width,height); MxCifQuadNode.minWidth = minWidth; @@ -375,6 +403,16 @@ public QuadTree.QuadNode getRoot() { return root; } + /** + * {@inheritDoc} + * + * Assumes height and width of 1 + */ + @Override + public boolean insert(double x, double y) { + return insert(x,y,1,1); + } + /** * Insert rectangle whose upper-left point is located at X,Y and has a height and width into tree. * @@ -383,13 +421,23 @@ public QuadTree.QuadNode getRoot() { * @param width Width of the rectangle. * @param height Height of the rectangle. */ - @SuppressWarnings("unchecked") - public boolean insert(float x, float y, float width, float height) { + public boolean insert(double x, double y, double width, double height) { XYPoint xyPoint = new XYPoint(x,y); AxisAlignedBoundingBox range = new AxisAlignedBoundingBox(xyPoint,width,height); + return root.insert((B)range); } + /** + * {@inheritDoc} + * + * Assumes height and width of 1 + */ + @Override + public boolean remove(double x, double y) { + return remove(x,y,1,1); + } + /** * Remove rectangle whose upper-left point is located at X,Y and has a height and width into tree. * @@ -398,30 +446,33 @@ public boolean insert(float x, float y, float width, float height) { * @param width Width of the rectangle. * @param height Height of the rectangle. */ - @SuppressWarnings("unchecked") - public boolean remove(float x, float y, float width, float height) { - XYPoint xyPoint = new XYPoint(x,y); - AxisAlignedBoundingBox range = new AxisAlignedBoundingBox(xyPoint,width,height); - return root.remove((B)range); + public boolean remove(double x, double y, double width, double height) { + XY_POINT.set(x,y); + RANGE.set(XY_POINT,width,height); + + return root.remove((B)RANGE); } /** * {@inheritDoc} */ @Override - public List queryRange(float x, float y, float width, float height) { + public Collection queryRange(double x, double y, double width, double height) { + if (root == null) + return Collections.EMPTY_LIST; + + XY_POINT.set(x,y); + RANGE.set(XY_POINT,width,height); + List geometricObjectsInRange = new LinkedList(); - if (root==null) return geometricObjectsInRange; - XYPoint xyPoint = new XYPoint(x,y); - AxisAlignedBoundingBox range = new AxisAlignedBoundingBox(xyPoint,width,height); - root.queryRange(range,geometricObjectsInRange); + root.queryRange(RANGE,geometricObjectsInRange); return geometricObjectsInRange; } protected static class MxCifQuadNode extends QuadNode { - protected static float minWidth = 1; - protected static float minHeight = 1; + protected static double minWidth = 1; + protected static double minHeight = 1; protected List aabbs = new LinkedList(); @@ -437,11 +488,14 @@ protected MxCifQuadNode(AxisAlignedBoundingBox aabb) { @Override protected boolean insert(AABB b) { // Ignore objects which do not belong in this quad tree - if (!aabb.intersectsBox(b)) return false; // object cannot be added - if (aabbs.contains(b)) return true; // already exists + if (!aabb.intersectsBox(b)) + return false; // object cannot be added + if (aabbs.contains(b)) + return true; // already exists // Subdivide then add the objects to whichever node will accept it - if (isLeaf()) subdivide(b); + if (isLeaf()) + subdivide(b); boolean inserted = false; if (isLeaf()) { @@ -467,10 +521,12 @@ protected boolean insert(AABB b) { @Override protected boolean remove(AABB b) { // If not in this AABB, don't do anything - if (!aabb.intersectsBox(b)) return false; + if (!aabb.intersectsBox(b)) + return false; // If in this AABB and in this node - if (aabbs.remove(b)) return true; + if (aabbs.remove(b)) + return true; // If this node has children if (!isLeaf()) { @@ -490,22 +546,22 @@ protected int size() { } private boolean subdivide(AABB b) { - float w = aabb.width/2; - float h = aabb.height/2; + double w = aabb.width/2d; + double h = aabb.height/2d; if (w(aabbNW); - XYPoint xyNE = new XYPoint(aabb.upperLeft.x+w,aabb.upperLeft.y); + XYPoint xyNE = new XYPoint(aabb.x+w,aabb.y); AxisAlignedBoundingBox aabbNE = new AxisAlignedBoundingBox(xyNE,w,h); northEast = new MxCifQuadNode(aabbNE); - XYPoint xySW = new XYPoint(aabb.upperLeft.x,aabb.upperLeft.y+h); + XYPoint xySW = new XYPoint(aabb.x,aabb.y+h); AxisAlignedBoundingBox aabbSW = new AxisAlignedBoundingBox(xySW,w,h); southWest = new MxCifQuadNode(aabbSW); - XYPoint xySE = new XYPoint(aabb.upperLeft.x+w,aabb.upperLeft.y+h); + XYPoint xySE = new XYPoint(aabb.x+w,aabb.y+h); AxisAlignedBoundingBox aabbSE = new AxisAlignedBoundingBox(xySE,w,h); southEast = new MxCifQuadNode(aabbSE); @@ -536,11 +592,13 @@ private boolean removeFromChildren(AABB b) { @Override protected void queryRange(AxisAlignedBoundingBox range, List geometricObjectsInRange) { // Automatically abort if the range does not collide with this quad - if (!aabb.intersectsBox(range)) return; + if (!aabb.intersectsBox(range)) + return; // Check objects at this level for (AABB b : aabbs) { - if (range.intersectsBox(b)) geometricObjectsInRange.add(b); + if (range.intersectsBox(b)) + geometricObjectsInRange.add(b); } // Otherwise, add the objects from the children @@ -560,19 +618,18 @@ public String toString() { StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(", "); builder.append("["); - for (AABB p : aabbs) { + for (AABB p : aabbs) builder.append(p).append(", "); - } builder.append("]"); return builder.toString(); } } } + protected static abstract class QuadNode implements Comparable> { - protected static abstract class QuadNode implements Comparable> { + protected final AxisAlignedBoundingBox aabb; - protected AxisAlignedBoundingBox aabb = null; protected QuadNode northWest = null; protected QuadNode northEast = null; protected QuadNode southWest = null; @@ -637,7 +694,6 @@ public int hashCode() { /** * {@inheritDoc} */ - @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { if (obj == null) @@ -672,24 +728,27 @@ public String toString() { } } - protected abstract static class GeometricObject { - // Nothing.. At this point - } + public static class XYPoint implements Comparable { - public static class XYPoint extends GeometricObject implements Comparable { + protected double x = Float.MIN_VALUE; + protected double y = Float.MIN_VALUE; - private float x = Float.MIN_VALUE; - private float y = Float.MIN_VALUE; + public XYPoint() { } - public XYPoint(float x, float y) { + public XYPoint(double x, double y) { this.x = x; this.y = y; } - public float getX() { + public void set(double x, double y) { + this.x = x; + this.y = y; + } + + public double getX() { return x; } - public float getY() { + public double getY() { return y; } @@ -722,11 +781,15 @@ public boolean equals(Object obj) { * {@inheritDoc} */ @Override - public int compareTo(XYPoint o) { - int xComp = X_COMPARATOR.compare(this, o); - if (xComp != 0) return xComp; + public int compareTo(Object o) { + if ((o instanceof XYPoint)==false) + throw new RuntimeException("Cannot compare object."); - return Y_COMPARATOR.compare(this, o); + XYPoint p = (XYPoint) o; + int xComp = X_COMPARATOR.compare(this, p); + if (xComp != 0) + return xComp; + return Y_COMPARATOR.compare(this, p); } /** @@ -743,19 +806,20 @@ public String toString() { } } - public static class AxisAlignedBoundingBox extends GeometricObject implements Comparable { + public static class AxisAlignedBoundingBox extends XYPoint { - private XYPoint upperLeft = null; - private float height = 0; - private float width = 0; + private double height = 0; + private double width = 0; - private float minX = 0; - private float minY = 0; - private float maxX = 0; - private float maxY = 0; + private double minX = 0; + private double minY = 0; + private double maxX = 0; + private double maxY = 0; - public AxisAlignedBoundingBox(XYPoint upperLeft, float width, float height) { - this.upperLeft = upperLeft; + public AxisAlignedBoundingBox() { } + + public AxisAlignedBoundingBox(XYPoint upperLeft, double width, double height) { + super(upperLeft.x, upperLeft.y); this.width = width; this.height = height; @@ -765,13 +829,21 @@ public AxisAlignedBoundingBox(XYPoint upperLeft, float width, float height) { maxY = upperLeft.y+height; } - public XYPoint getUpperLeft() { - return upperLeft; + public void set(XYPoint upperLeft, double width, double height) { + set(upperLeft.x, upperLeft.y); + this.width = width; + this.height = height; + + minX = upperLeft.x; + minY = upperLeft.y; + maxX = upperLeft.x+width; + maxY = upperLeft.y+height; } - public float getHeight() { + + public double getHeight() { return height; } - public float getWidth() { + public double getWidth() { return width; } @@ -822,7 +894,7 @@ public boolean intersectsBox(AxisAlignedBoundingBox b) { */ @Override public int hashCode() { - int hash = upperLeft.hashCode(); + int hash = super.hashCode(); hash = hash * 13 + (int)height; hash = hash * 19 + (int)width; return hash; @@ -846,15 +918,19 @@ public boolean equals(Object obj) { * {@inheritDoc} */ @Override - public int compareTo(AxisAlignedBoundingBox o) { - int p = upperLeft.compareTo(o.upperLeft); + public int compareTo(Object o) { + if ((o instanceof AxisAlignedBoundingBox)==false) + throw new RuntimeException("Cannot compare object."); + + AxisAlignedBoundingBox a = (AxisAlignedBoundingBox) o; + int p = super.compareTo(a); if (p!=0) return p; - if (height>o.height) return 1; - if (heighta.height) return 1; + if (heighto.width) return 1; - if (widtha.width) return 1; + if (width String getString(QuadTree tree) { + public static String getString(QuadTree tree) { if (tree.getRoot() == null) return "Tree has no nodes."; return getString(tree.getRoot(), "", true); } - private static String getString(QuadNode node, String prefix, boolean isTail) { + private static String getString(QuadNode node, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); builder.append(prefix + (isTail ? "└── " : "├── ") + " node={" + node.toString() + "}\n"); diff --git a/src/com/jwetherell/algorithms/data_structures/Queue.java b/src/com/jwetherell/algorithms/data_structures/Queue.java index 3ea2e733..92962a4d 100644 --- a/src/com/jwetherell/algorithms/data_structures/Queue.java +++ b/src/com/jwetherell/algorithms/data_structures/Queue.java @@ -1,5 +1,15 @@ package com.jwetherell.algorithms.data_structures; +import com.jwetherell.algorithms.data_structures.interfaces.IQueue; + +/** + * In computer science, a queue is a particular kind of abstract data type or collection in which the entities in the collection are kept in order and the principal (or only) operations + * on the collection are the addition of entities to the rear terminal position, known as enqueue, and removal of entities from the front terminal position, known as dequeue. + *

+ * @see Queue (Wikipedia) + *
+ * @author Justin Wetherell + */ @SuppressWarnings("unchecked") public interface Queue extends IQueue { @@ -10,7 +20,7 @@ public interface Queue extends IQueue { */ public static class ArrayQueue implements Queue { - private static final int MINIMUM_SIZE = 10; + private static final int MINIMUM_SIZE = 1024; private T[] array = (T[]) new Object[MINIMUM_SIZE]; private int lastIndex = 0; @@ -21,21 +31,10 @@ public static class ArrayQueue implements Queue { */ @Override public boolean offer(T value) { - int currentSize = lastIndex - firstIndex; - if (currentSize >= array.length) { - int growSize = (currentSize + (currentSize>>1)); - T[] temp = (T[]) new Object[growSize]; - // Since the array can wrap around, make sure you grab the first chunk - int adjLast = lastIndex%array.length; - if (adjLast<=firstIndex) { - System.arraycopy(array, 0, temp, array.length-adjLast, adjLast+1); - } - System.arraycopy(array, firstIndex, temp, 0, array.length-firstIndex); - array = temp; - lastIndex = (lastIndex - firstIndex); - firstIndex = 0; - } - array[lastIndex%array.length] = value; + if (size() >= array.length) + grow(size()); + + array[lastIndex % array.length] = value; lastIndex++; return true; } @@ -48,8 +47,8 @@ public T poll() { int size = lastIndex - firstIndex; if (size < 0) return null; - T t = array[firstIndex%array.length]; - array[firstIndex%array.length] = null; + T t = array[firstIndex % array.length]; + array[firstIndex % array.length] = null; firstIndex++; size = lastIndex - firstIndex; @@ -59,20 +58,9 @@ public T poll() { firstIndex = 0; } - int shrinkSize = (size + (size<<1)); - if (shrinkSize >= MINIMUM_SIZE && (array.length > shrinkSize)) { - T[] temp = (T[]) new Object[shrinkSize]; - // Since the array can wrap around, make sure you grab the first chunk - int adjLast = lastIndex%array.length; - int endIndex = (lastIndex>array.length)?array.length:lastIndex; - if (adjLast<=firstIndex) { - System.arraycopy(array, 0, temp, array.length-firstIndex, adjLast); - } - System.arraycopy(array, firstIndex, temp, 0, endIndex-firstIndex); - array = temp; - lastIndex = (lastIndex - firstIndex); - firstIndex = 0; - } + int shrinkSize = array.length>>1; + if (shrinkSize >= MINIMUM_SIZE && size < shrinkSize) + shrink(); return t; } @@ -82,7 +70,7 @@ public T poll() { */ @Override public T peek() { - return array[firstIndex%array.length]; + return array[firstIndex % array.length]; } /** @@ -90,9 +78,10 @@ public T peek() { */ @Override public boolean remove(T value) { - for (int i = firstIndex; i < lastIndex; i++) { - T obj = array[i%array.length]; - if (obj.equals(value)) return remove(i); + for (int i=0; i < array.length; i++) { + T obj = array[i]; + // if obj is null, it should return false (not NPE) + if (value.equals(obj)) return remove(i); } return false; } @@ -101,12 +90,12 @@ private boolean remove(int index) { if (index<0 || index >= array.length) return false; if (index==firstIndex) return (poll()!=null); - int adjIndex = index%array.length; - int adjLastIndex = (lastIndex-1)%array.length; + int adjIndex = index % array.length; + int adjLastIndex = (lastIndex-1) % array.length; if (adjIndex != adjLastIndex) { // Shift the array down one spot System.arraycopy(array, index+1, array, index, (array.length - (index+1))); - if (adjLastIndex= MINIMUM_SIZE && (array.length > shrinkSize)) { - T[] temp = (T[]) new Object[shrinkSize]; - // Since the array can wrap around, make sure you grab the first chunk - int adjLast = lastIndex%array.length; - int endIndex = (lastIndex>array.length)?array.length:lastIndex; - if (adjLast<=firstIndex) { - System.arraycopy(array, 0, temp, array.length-firstIndex, adjLast); - } - System.arraycopy(array, firstIndex, temp, 0, endIndex-firstIndex); - array = temp; - lastIndex = (lastIndex - firstIndex); - firstIndex = 0; - } + int shrinkSize = array.length>>1; + if (shrinkSize >= MINIMUM_SIZE && size() < shrinkSize) + shrink(); lastIndex--; return true; } + // Triple the size of the underlying array and rearrange to make sequential + private void grow(int size) { + int growSize = (size + (size<<1)); + T[] temp = (T[]) new Object[growSize]; + // Since the array can wrap around, make sure you grab the first chunk + int adjLast = lastIndex % array.length; + if (adjLast > 0 && adjLast <= firstIndex) { + System.arraycopy(array, 0, temp, array.length-adjLast, adjLast); + } + // Copy the remaining + System.arraycopy(array, firstIndex, temp, 0, array.length - firstIndex); + array = null; + array = temp; + lastIndex = (lastIndex - firstIndex); + firstIndex = 0; + } + + // Shrink the array by 50% and rearrange to make sequential + private void shrink() { + int shrinkSize = array.length>>1; + T[] temp = (T[]) new Object[shrinkSize]; + // Since the array can wrap around, make sure you grab the first chunk + int adjLast = lastIndex % array.length; + int endIndex = (lastIndex>array.length) ? array.length : lastIndex; + if (adjLast <= firstIndex) { + System.arraycopy(array, 0, temp, array.length - firstIndex, adjLast); + } + // Copy the remaining + System.arraycopy(array, firstIndex, temp, 0, endIndex-firstIndex); + array = null; + array = temp; + lastIndex = (lastIndex - firstIndex); + firstIndex = 0; + } + /** * {@inheritDoc} */ @@ -148,9 +160,10 @@ public void clear() { */ @Override public boolean contains(T value) { - for (int i = firstIndex; i < lastIndex; i++) { - T obj = array[i%array.length]; - if (obj.equals(value)) return true; + for (int i=0; i < array.length; i++) { + T obj = array[i]; + // if obj is null, it should return false (not NPE) + if (value.equals(obj)) return true; } return false; } @@ -550,7 +563,7 @@ public boolean hasNext() { public T next() { if (queue.firstIndex+index < queue.lastIndex) { last = queue.firstIndex+index; - return queue.array[queue.firstIndex+index++]; + return queue.array[(queue.firstIndex + index++) % queue.array.length]; } return null; } diff --git a/src/com/jwetherell/algorithms/data_structures/RadixTrie.java b/src/com/jwetherell/algorithms/data_structures/RadixTrie.java index 0dd7335e..012f0d01 100644 --- a/src/com/jwetherell/algorithms/data_structures/RadixTrie.java +++ b/src/com/jwetherell/algorithms/data_structures/RadixTrie.java @@ -1,5 +1,7 @@ package com.jwetherell.algorithms.data_structures; +import com.jwetherell.algorithms.data_structures.interfaces.IMap; + /** * A radix trie or radix tree is a space-optimized trie data structure where * each node with only one child is merged with its child. The result is that @@ -8,13 +10,13 @@ * This makes them much more efficient for small sets (especially if the strings * are long) and for sets of strings that share long prefixes. This particular * radix tree is used to represent an associative array. - * + *

* This implementation is a composition of a PatriciaTrie as the backing * structure. - * - * http://en.wikipedia.org/wiki/Radix_tree - * http://en.wikipedia.org/wiki/Associative_array - * + *

+ * @see Radix Tree / Patricia Trie (Wikipedia) + * @see Associative Array (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") @@ -36,7 +38,8 @@ public V put(K key, V value) { if (node!=null && node instanceof RadixNode) { RadixNode radixNode = (RadixNode) node; - if (radixNode.value!=null) prev = radixNode.value; + if (radixNode.value!=null) + prev = radixNode.value; radixNode.value = value; } @@ -75,6 +78,7 @@ public V remove(K key) { if (node instanceof RadixNode) { RadixNode rn = (RadixNode)node; value = rn.value; + rn.value = null; } trie.remove(node); } @@ -104,12 +108,14 @@ public int size() { public boolean validate() { java.util.Set keys = new java.util.HashSet(); PatriciaTrie.Node node = trie.root; - if (node!=null && !validate(node,"",keys)) return false; + if (node!=null && !validate(node,"",keys)) + return false; return (keys.size()==size()); } private boolean validate(PatriciaTrie.Node node, String string, java.util.Set keys) { - if (!(node instanceof RadixNode)) return false; + if (!(node instanceof RadixNode)) + return false; RadixNode tmn = (RadixNode)node; @@ -120,13 +126,16 @@ private boolean validate(PatriciaTrie.Node node, String string, java.util.Set if (tmn.type == PatriciaTrie.WHITE) { K k = (K)s; V v = tmn.value; - if (k==null || v==null) return false; - if (keys.contains(k)) return false; + if (k==null || v==null) + return false; + if (keys.contains(k)) + return false; keys.add(k); } for (int i=0; i String getString(PatriciaTrie.Node if (node instanceof RadixNode) { RadixNode radix = (RadixNode) node; builder.append(prefix - + (isTail ? "└── " : "├── ") - + ((radix.string != null) ? "(" + String.valueOf(radix.string) + ") " + "[" - + ((node.type == PatriciaTrie.WHITE) ? "WHITE" : "BLACK") + "] " + string - + ((radix.value != null) ? " = " + radix.value : "") : "[" + node.type + "]") + "\n"); + + (isTail ? "└── " : "├── ") + + ((radix.string != null) ? + "(" + String.valueOf(radix.string) + ") " + + "[" + ((node.type == PatriciaTrie.WHITE) ? "WHITE" : "BLACK") + "] " + + string + + ((radix.value != null) ? " = " + radix.value : "") + : + "[" + ((node.type == PatriciaTrie.WHITE) ? "WHITE" : "BLACK") + "]") + "\n"); } if (node.getChildrenSize() > 0) { for (int i = 0; i < node.getChildrenSize() - 1; i++) { builder.append(getString(node.getChild(i), prefix + (isTail ? " " : "│ "), string, false)); } if (node.getChildrenSize() >= 1) { - builder.append(getString(node.getChild(node.getChildrenSize() - 1), prefix - + (isTail ? " " : "│ "), string, true)); + builder.append(getString(node.getChild(node.getChildrenSize() - 1), prefix + (isTail ? " " : "│ "), string, true)); } } return builder.toString(); @@ -243,7 +255,8 @@ public JavaCompatibleIteratorWrapper(RadixTrie map, java.util.Iterator next() { - if (iter==null) return null; + if (iter==null) + return null; lastEntry = iter.next(); return lastEntry; @@ -263,7 +277,8 @@ public java.util.Map.Entry next() { */ @Override public void remove() { - if (iter==null || lastEntry==null) return; + if (iter==null || lastEntry==null) + return; map.remove(lastEntry.getKey()); iter.remove(); @@ -356,7 +371,8 @@ private void levelOrder(PatriciaTrie.Node node, String string, java.util.Set tmn = null; if (node instanceof RadixNode) { tmn = (RadixNode)node; - if (tmn.string!=null) builder.append(tmn.string); + if (tmn.string!=null) + builder.append(tmn.string); if (tmn.type == PatriciaTrie.WHITE) { K s = (K)builder.toString(); java.util.Map.Entry entry = new JavaCompatibleMapEntry(s, tmn.value); diff --git a/src/com/jwetherell/algorithms/data_structures/RedBlackTree.java b/src/com/jwetherell/algorithms/data_structures/RedBlackTree.java index a6baf656..c3c7400c 100644 --- a/src/com/jwetherell/algorithms/data_structures/RedBlackTree.java +++ b/src/com/jwetherell/algorithms/data_structures/RedBlackTree.java @@ -14,13 +14,13 @@ * often compared with AVL trees. AVL trees are more rigidly balanced, they are * faster than red-black trees for lookup intensive applications. However, * red-black trees are faster for insertion and removal. - * - * http://en.wikipedia.org/wiki/Red%E2%80%93black_tree - * + *

+ * @see Red-Black Tree (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") -public class RedBlackTree> extends BinarySearchTree implements BinarySearchTree.INodeCreator { +public class RedBlackTree> extends BinarySearchTree { protected static final boolean BLACK = false; protected static final boolean RED = true; @@ -29,7 +29,15 @@ public class RedBlackTree> extends BinarySearchTree i * Default constructor. */ public RedBlackTree() { - this.creator = this; + this.creator = new BinarySearchTree.INodeCreator() { + /** + * {@inheritDoc} + */ + @Override + public BinarySearchTree.Node createNewNode(BinarySearchTree.Node parent, T id) { + return (new RedBlackNode(parent, id, BLACK)); + } + }; } /** @@ -44,67 +52,55 @@ public RedBlackTree(INodeCreator creator) { */ @Override protected Node addValue(T id) { - RedBlackNode nodeAdded = null; - boolean added = false; if (root == null) { // Case 1 - The current node is at the root of the tree. - if (this.creator == null) { - root = new RedBlackNode(null, id, BLACK); - root.lesser = new RedBlackNode(root, null, BLACK); - root.greater = new RedBlackNode(root, null, BLACK); + + // Defaulted to black in our creator + root = this.creator.createNewNode(null, id); + root.lesser = this.creator.createNewNode(root, null); + root.greater = this.creator.createNewNode(root, null); + + size++; + return root; + } + + RedBlackNode nodeAdded = null; + // Insert node like a BST would + Node node = root; + while (node != null) { + if (node.id == null) { + node.id = id; + ((RedBlackNode) node).color = RED; + + // Defaulted to black in our creator + node.lesser = this.creator.createNewNode(node, null); + node.greater = this.creator.createNewNode(node, null); + + nodeAdded = (RedBlackNode) node; + break; + } else if (id.compareTo(node.id) <= 0) { + node = node.lesser; } else { - root = this.creator.createNewNode(null, id); - ((RedBlackNode) root).color = BLACK; - root.lesser = this.creator.createNewNode(root, null); - ((RedBlackNode) root.lesser).color = BLACK; - root.greater = this.creator.createNewNode(root, null); - ((RedBlackNode) root.greater).color = BLACK; - } - nodeAdded = (RedBlackNode) root; - added = true; - } else { - // Insert node like a BST would - Node node = root; - while (node != null) { - if (node.id == null) { - node.id = id; - ((RedBlackNode) node).color = RED; - if (this.creator == null) { - node.lesser = new RedBlackNode(node, null, BLACK); - node.greater = new RedBlackNode(node, null, BLACK); - } else { - node.lesser = this.creator.createNewNode(node, null); - ((RedBlackNode) node.lesser).color = BLACK; - node.greater = this.creator.createNewNode(node, null); - ((RedBlackNode) node.greater).color = BLACK; - } - nodeAdded = (RedBlackNode) node; - added = true; - break; - } else if (id.compareTo(node.id) <= 0) { - node = node.lesser; - } else { - node = node.greater; - } + node = node.greater; } } - if (added == true) { + if (nodeAdded != null) balanceAfterInsert(nodeAdded); - size++; - } + size++; return nodeAdded; } /** * Post insertion balancing algorithm. * - * @param node + * @param begin * to begin balancing at. * @return True if balanced. */ - private void balanceAfterInsert(RedBlackNode node) { + private void balanceAfterInsert(RedBlackNode begin) { + RedBlackNode node = begin; RedBlackNode parent = (RedBlackNode) node.parent; if (parent == null) { @@ -120,7 +116,7 @@ private void balanceAfterInsert(RedBlackNode node) { } RedBlackNode grandParent = node.getGrandParent(); - RedBlackNode uncle = node.getUncle(); + RedBlackNode uncle = node.getUncle(grandParent); if (parent.color == RED && uncle.color == RED) { // Case 3 - If both the parent and the uncle are red, then both of // them can be repainted black and the grandparent becomes @@ -132,43 +128,44 @@ private void balanceAfterInsert(RedBlackNode node) { grandParent.color = RED; balanceAfterInsert(grandParent); } - } else { - if (parent.color == RED && uncle.color == BLACK) { - // Case 4 - The parent is red but the uncle is black; also, the - // current node is the right child of parent, and parent in turn - // is the left child of its parent grandparent. - if (node.equals(parent.greater) && parent.equals(grandParent.lesser)) { - // right-left - rotateLeft(parent); - node = (RedBlackNode) node.lesser; - - grandParent = node.getGrandParent(); - parent = (RedBlackNode) node.parent; - uncle = node.getUncle(); - } else if (node.equals(parent.lesser) && parent.equals(grandParent.greater)) { - // left-right - rotateRight(parent); - node = (RedBlackNode) node.greater; - - grandParent = node.getGrandParent(); - parent = (RedBlackNode) node.parent; - uncle = node.getUncle(); - } + return; + } + + if (parent.color == RED && uncle.color == BLACK) { + // Case 4 - The parent is red but the uncle is black; also, the + // current node is the right child of parent, and parent in turn + // is the left child of its parent grandparent. + if (node == parent.greater && parent == grandParent.lesser) { + // right-left + rotateLeft(parent); + + node = (RedBlackNode) node.lesser; + parent = (RedBlackNode) node.parent; + grandParent = node.getGrandParent(); + uncle = node.getUncle(grandParent); + } else if (node == parent.lesser && parent == grandParent.greater) { + // left-right + rotateRight(parent); + + node = (RedBlackNode) node.greater; + parent = (RedBlackNode) node.parent; + grandParent = node.getGrandParent(); + uncle = node.getUncle(grandParent); } + } - if (parent.color == RED && uncle.color == BLACK) { - // Case 5 - The parent is red but the uncle is black, the - // current node is the left child of parent, and parent is the - // left child of its parent G. - parent.color = BLACK; - grandParent.color = RED; - if (node.equals(parent.lesser) && parent.equals(grandParent.lesser)) { - // left-left - rotateRight(grandParent); - } else if (node.equals(parent.greater) && parent.equals(grandParent.greater)) { - // right-right - rotateLeft(grandParent); - } + if (parent.color == RED && uncle.color == BLACK) { + // Case 5 - The parent is red but the uncle is black, the + // current node is the left child of parent, and parent is the + // left child of its parent G. + parent.color = BLACK; + grandParent.color = RED; + if (node == parent.lesser && parent == grandParent.lesser) { + // left-left + rotateRight(grandParent); + } else if (node == parent.greater && parent == grandParent.greater) { + // right-right + rotateLeft(grandParent); } } } @@ -178,13 +175,14 @@ private void balanceAfterInsert(RedBlackNode node) { */ @Override protected Node removeNode(Node node) { + if (node == null) return node; + RedBlackNode nodeToRemoved = (RedBlackNode)node; - if (nodeToRemoved == null) return nodeToRemoved; if (nodeToRemoved.isLeaf()) { // No children nodeToRemoved.id = null; - if (nodeToRemoved.parent == null) { + if (nodeToRemoved == root) { root = null; } else { nodeToRemoved.id = null; @@ -192,44 +190,53 @@ protected Node removeNode(Node node) { nodeToRemoved.lesser = null; nodeToRemoved.greater = null; } - } else { - // Keep the id and assign it to the replacement node - T id = nodeToRemoved.id; - - // At least one child - RedBlackNode lesser = (RedBlackNode) nodeToRemoved.lesser; - RedBlackNode greater = (RedBlackNode) nodeToRemoved.greater; - if (lesser.id != null && greater.id != null) { - // Two children - RedBlackNode greatestInLesser = (RedBlackNode) this.getGreatest(lesser); - if (greatestInLesser == null || greatestInLesser.id == null) greatestInLesser = lesser; - // Replace node with greatest in his lesser tree, which leaves - // us with only one child - replaceValueOnly(nodeToRemoved, greatestInLesser); - nodeToRemoved = greatestInLesser; - } - // Handle one child - RedBlackNode child = (RedBlackNode) ((nodeToRemoved.lesser.id != null) ? nodeToRemoved.lesser : nodeToRemoved.greater); - if (nodeToRemoved.color == BLACK) { - if (child.color == BLACK) nodeToRemoved.color = RED; - boolean result = balanceAfterDelete(nodeToRemoved); - if (!result) return nodeToRemoved; - } - // Replacing node with child - replaceWithChild(nodeToRemoved, child); - // Add the id to the child because it represents the node - // that was removed. - child.id = id; - if (root.equals(nodeToRemoved)) { - root.parent = null; - ((RedBlackNode)root).color = BLACK; - // If we replaced the root with a leaf, just null out root - if (nodeToRemoved.isLeaf()) root = null; - } - nodeToRemoved = child; + size--; + return nodeToRemoved; + } + + // At least one child + + // Keep the id and assign it to the replacement node + T id = nodeToRemoved.id; + RedBlackNode lesser = (RedBlackNode) nodeToRemoved.lesser; + RedBlackNode greater = (RedBlackNode) nodeToRemoved.greater; + if (lesser.id != null && greater.id != null) { + // Two children + RedBlackNode greatestInLesser = (RedBlackNode) this.getGreatest(lesser); + if (greatestInLesser == null || greatestInLesser.id == null) + greatestInLesser = lesser; + + // Replace node with greatest in his lesser tree, which leaves us with only one child + replaceValueOnly(nodeToRemoved, greatestInLesser); + nodeToRemoved = greatestInLesser; + lesser = (RedBlackNode) nodeToRemoved.lesser; + greater = (RedBlackNode) nodeToRemoved.greater; + } + + // Handle one child + RedBlackNode child = (RedBlackNode) ((lesser.id != null) ? lesser : greater); + if (nodeToRemoved.color == BLACK) { + if (child.color == BLACK) + nodeToRemoved.color = RED; + boolean result = balanceAfterDelete(nodeToRemoved); + if (!result) + return nodeToRemoved; } + // Replacing node with child + replaceWithChild(nodeToRemoved, child); + // Add the id to the child because it represents the node that was removed. + child.id = id; + if (root == nodeToRemoved) { + root.parent = null; + ((RedBlackNode)root).color = BLACK; + // If we replaced the root with a leaf, just null out root + if (nodeToRemoved.isLeaf()) + root = null; + } + nodeToRemoved = child; + size--; return nodeToRemoved; } @@ -261,10 +268,12 @@ private void replaceWithChild(RedBlackNode nodeToReplace, RedBlackNode nod nodeToReplace.color = nodeToReplaceWith.color; nodeToReplace.lesser = nodeToReplaceWith.lesser; - if (nodeToReplace.lesser!=null) nodeToReplace.lesser.parent = nodeToReplace; + if (nodeToReplace.lesser!=null) + nodeToReplace.lesser.parent = nodeToReplace; nodeToReplace.greater = nodeToReplaceWith.greater; - if (nodeToReplace.greater!=null) nodeToReplace.greater.parent = nodeToReplace; + if (nodeToReplace.greater!=null) + nodeToReplace.greater.parent = nodeToReplace; } /** @@ -286,80 +295,89 @@ private boolean balanceAfterDelete(RedBlackNode node) { // Case 2 - sibling is red. parent.color = RED; sibling.color = BLACK; - if (node.equals(parent.lesser)) { + if (node == parent.lesser) { rotateLeft(parent); + // Rotation, need to update parent/sibling parent = (RedBlackNode) node.parent; sibling = node.getSibling(); - } else if (node.equals(parent.greater)) { + } else if (node == parent.greater) { rotateRight(parent); + // Rotation, need to update parent/sibling parent = (RedBlackNode) node.parent; sibling = node.getSibling(); } else { - System.err.println("Yikes! I'm not related to my parent."); - return false; + throw new RuntimeException("Yikes! I'm not related to my parent. " + node.toString()); } } - if (parent.color == BLACK && sibling.color == BLACK + if (parent.color == BLACK + && sibling.color == BLACK && ((RedBlackNode) sibling.lesser).color == BLACK && ((RedBlackNode) sibling.greater).color == BLACK ) { // Case 3 - parent, sibling, and sibling's children are black. sibling.color = RED; - boolean result = balanceAfterDelete(parent); - if (!result) return false; - } else if (parent.color == RED && sibling.color == BLACK - && ((RedBlackNode) sibling.lesser).color == BLACK - && ((RedBlackNode) sibling.greater).color == BLACK + return balanceAfterDelete(parent); + } + + if (parent.color == RED + && sibling.color == BLACK + && ((RedBlackNode) sibling.lesser).color == BLACK + && ((RedBlackNode) sibling.greater).color == BLACK ) { // Case 4 - sibling and sibling's children are black, but parent is red. sibling.color = RED; parent.color = BLACK; - } else { - if (sibling.color == BLACK) { - // Case 5 - sibling is black, sibling's left child is red, - // sibling's right child is black, and node is the left child of - // its parent. - if (node.equals(parent.lesser) - && ((RedBlackNode) sibling.lesser).color == RED - && ((RedBlackNode) sibling.greater).color == BLACK - ) { - sibling.color = RED; - ((RedBlackNode) sibling.lesser).color = RED; - rotateRight(sibling); - // Rotation, need to update parent/sibling - parent = (RedBlackNode) node.parent; - sibling = node.getSibling(); - } else if (node.equals(parent.greater) - && ((RedBlackNode) sibling.lesser).color == BLACK - && ((RedBlackNode) sibling.greater).color == RED - ) { - sibling.color = RED; - ((RedBlackNode) sibling.greater).color = RED; - rotateLeft(sibling); - // Rotation, need to update parent/sibling - parent = (RedBlackNode) node.parent; - sibling = node.getSibling(); - } - } + return true; + } - // Case 6 - sibling is black, sibling's right child is red, and node - // is the left child of its parent. - sibling.color = parent.color; - parent.color = BLACK; - if (node.equals(parent.lesser)) { - ((RedBlackNode) sibling.greater).color = BLACK; - rotateLeft(node.parent); - } else if (node.equals(parent.greater)) { - ((RedBlackNode) sibling.lesser).color = BLACK; - rotateRight(node.parent); - } else { - System.err.println("Yikes! I'm not related to my parent. " + node.toString()); - return false; + if (sibling.color == BLACK) { + // Case 5 - sibling is black, sibling's left child is red, + // sibling's right child is black, and node is the left child of + // its parent. + if (node == parent.lesser + && ((RedBlackNode) sibling.lesser).color == RED + && ((RedBlackNode) sibling.greater).color == BLACK + ) { + sibling.color = RED; + ((RedBlackNode) sibling.lesser).color = RED; + + rotateRight(sibling); + + // Rotation, need to update parent/sibling + parent = (RedBlackNode) node.parent; + sibling = node.getSibling(); + } else if (node == parent.greater + && ((RedBlackNode) sibling.lesser).color == BLACK + && ((RedBlackNode) sibling.greater).color == RED + ) { + sibling.color = RED; + ((RedBlackNode) sibling.greater).color = RED; + + rotateLeft(sibling); + + // Rotation, need to update parent/sibling + parent = (RedBlackNode) node.parent; + sibling = node.getSibling(); } } + + // Case 6 - sibling is black, sibling's right child is red, and node + // is the left child of its parent. + sibling.color = parent.color; + parent.color = BLACK; + if (node == parent.lesser) { + ((RedBlackNode) sibling.greater).color = BLACK; + rotateLeft(node.parent); + } else if (node == parent.greater) { + ((RedBlackNode) sibling.lesser).color = BLACK; + rotateRight(node.parent); + } else { + throw new RuntimeException("Yikes! I'm not related to my parent. " + node.toString()); + } + return true; } @@ -402,19 +420,23 @@ protected boolean validateNode(Node node) { if (!lesser.isLeaf()) { // Check BST property boolean lesserCheck = lesser.id.compareTo(rbNode.id) <= 0; - if (!lesserCheck) return false; + if (!lesserCheck) + return false; // Check red-black property lesserCheck = this.validateNode(lesser); - if (!lesserCheck) return false; + if (!lesserCheck) + return false; } if (!greater.isLeaf()) { // Check BST property boolean greaterCheck = greater.id.compareTo(rbNode.id) > 0; - if (!greaterCheck) return false; + if (!greaterCheck) + return false; // Check red-black property greaterCheck = this.validateNode(greater); - if (!greaterCheck) return false; + if (!greaterCheck) + return false; } return true; @@ -436,14 +458,6 @@ public String toString() { return RedBlackTreePrinter.getString(this); } - /** - * {@inheritDoc} - */ - @Override - public Node createNewNode(Node parent, T id) { - return (new RedBlackNode(parent, id, BLACK)); - } - protected static class RedBlackNode> extends Node { protected boolean color = BLACK; @@ -458,32 +472,38 @@ protected RedBlackNode getGrandParent() { return (RedBlackNode) parent.parent; } - protected RedBlackNode getUncle() { - RedBlackNode grandParent = getGrandParent(); + protected RedBlackNode getUncle(RedBlackNode grandParent) { if (grandParent == null) return null; - if (grandParent.lesser != null && grandParent.lesser.equals(parent)) { + if (grandParent.lesser != null && grandParent.lesser == parent) { return (RedBlackNode) grandParent.greater; - } else if (grandParent.greater != null && grandParent.greater.equals(parent)) { + } else if (grandParent.greater != null && grandParent.greater == parent) { return (RedBlackNode) grandParent.lesser; } return null; } + protected RedBlackNode getUncle() { + RedBlackNode grandParent = getGrandParent(); + return getUncle(grandParent); + } + protected RedBlackNode getSibling() { - if (parent == null) return null; - if (parent.lesser.equals(this)) { + if (parent == null) + return null; + if (parent.lesser == this) { return (RedBlackNode) parent.greater; - } else if (parent.greater.equals(this)) { + } else if (parent.greater == this) { return (RedBlackNode) parent.lesser; } else { - System.err.println("Yikes! I'm not my parents child."); + throw new RuntimeException("Yikes! I'm not related to my parent. " + this.toString()); } - return null; } protected boolean isLeaf() { - if (lesser != null) return false; - if (greater != null) return false; + if (lesser != null) + return false; + if (greater != null) + return false; return true; } @@ -530,12 +550,10 @@ private static > String getString(RedBlackNode node, } if (children != null) { for (int i = 0; i < children.size() - 1; i++) { - builder.append(getString((RedBlackNode) children.get(i), prefix + (isTail ? " " : "│ "), - false)); + builder.append(getString((RedBlackNode) children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() >= 1) { - builder.append(getString((RedBlackNode) children.get(children.size() - 1), prefix - + (isTail ? " " : "│ "), true)); + builder.append(getString((RedBlackNode) children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } diff --git a/src/com/jwetherell/algorithms/data_structures/SegmentTree.java b/src/com/jwetherell/algorithms/data_structures/SegmentTree.java index 541b8870..fb516d8e 100644 --- a/src/com/jwetherell/algorithms/data_structures/SegmentTree.java +++ b/src/com/jwetherell/algorithms/data_structures/SegmentTree.java @@ -1,11 +1,13 @@ package com.jwetherell.algorithms.data_structures; +import java.math.BigDecimal; +import java.math.BigInteger; import java.security.InvalidParameterException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -16,15 +18,16 @@ * stored segments contain a given point. It is, in principle, a static * structure; that is, its content cannot be modified once the structure is * built. - * - * http://en.wikipedia.org/wiki/Segment_tree - * + *

* This class is meant to be somewhat generic, all you'd have to do is extend * the Data abstract class to store your custom data. I've also included a range * minimum, range maximum, range sum, and interval stabbing implementations. - * + *

+ * @see Segment Tree (Wikipedia) + *
* @author Justin Wetherell */ +@SuppressWarnings("unchecked") public abstract class SegmentTree { protected Segment root = null; @@ -33,7 +36,7 @@ public abstract class SegmentTree { * Stabbing query * * @param index - * to query for. + * index to query * @return data at index. */ public abstract D query(long index); @@ -42,9 +45,9 @@ public abstract class SegmentTree { * Range query * * @param start - * of range to query for. + * start of range (inclusive) * @param end - * of range to query for. + * end of range to (inclusive) * @return data for range. */ public abstract D query(long start, long end); @@ -65,7 +68,7 @@ public abstract static class Data implements Comparable { protected long end = Long.MAX_VALUE; /** - * Constructor for data at index. + * Constructor for data at index * * @param index * of data. @@ -76,12 +79,12 @@ public Data(long index) { } /** - * Constructor for data at range. + * Constructor for data at range (inclusive) * * @param start - * of range for data. + * start of range for data. * @param end - * of range for data. + * end of range for data. */ public Data(long start, long end) { this.start = start; @@ -89,7 +92,7 @@ public Data(long start, long end) { } /** - * Clear the indices. + * Clear the indices */ public void clear() { start = Long.MIN_VALUE; @@ -97,10 +100,10 @@ public void clear() { } /** - * Combined this data with data. + * Combined this data with the Data parameter * * @param data - * to combined with. + * Data to combined * @return Data which represents the combination. */ public abstract Data combined(Data data); @@ -115,20 +118,20 @@ public void clear() { /** * Query inside this data object. * - * @param start - * of range to query for. - * @param end - * of range to query for. + * @param startOfRange + * start of range (inclusive) + * @param endOfRange + * end of range (inclusive) * @return Data queried for or NULL if it doesn't match the query. */ - public abstract Data query(long start, long end); + public abstract Data query(long startOfRange, long endOfRange); /** * {@inheritDoc} */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append(start).append("->").append(end); return builder.toString(); } @@ -151,14 +154,10 @@ public int compareTo(Data d) { */ public static final class QuadrantData extends Data { + public long quad0 = 0; public long quad1 = 0; public long quad2 = 0; public long quad3 = 0; - public long quad4 = 0; - - public QuadrantData(long index) { - super(index); - } public QuadrantData(long start, long end) { super(start, end); @@ -167,10 +166,10 @@ public QuadrantData(long start, long end) { public QuadrantData(long index, long quad1, long quad2, long quad3, long quad4) { super(index); - this.quad1 = quad1; - this.quad2 = quad2; - this.quad3 = quad3; - this.quad4 = quad4; + this.quad0 = quad1; + this.quad1 = quad2; + this.quad2 = quad3; + this.quad3 = quad4; } /** @@ -180,10 +179,33 @@ public QuadrantData(long index, long quad1, long quad2, long quad3, long quad4) public void clear() { super.clear(); + quad0 = 0; quad1 = 0; quad2 = 0; quad3 = 0; - quad4 = 0; + } + + /** + * {@inheritDoc} + */ + @Override + public QuadrantData copy() { + final QuadrantData copy = new QuadrantData(start, end); + copy.quad0 = this.quad0; + copy.quad1 = this.quad1; + copy.quad2 = this.quad2; + copy.quad3 = this.quad3; + return copy; + } + + /** + * {@inheritDoc} + */ + @Override + public Data query(long startOfQuery, long endOfQuery) { + if (endOfQuery < this.start || startOfQuery > this.end) + return null; + return copy(); } /** @@ -199,38 +221,35 @@ public Data combined(Data data) { return this; } - /** - * Combined specific to quadrant data. - * - * @param data - * to combined. - */ private void combined(QuadrantData data) { + this.quad0 += data.quad0; this.quad1 += data.quad1; this.quad2 += data.quad2; this.quad3 += data.quad3; - this.quad4 += data.quad4; } /** * {@inheritDoc} */ @Override - public QuadrantData copy() { - QuadrantData copy = new QuadrantData(start, end); - copy.quad1 = this.quad1; - copy.quad2 = this.quad2; - copy.quad3 = this.quad3; - copy.quad4 = this.quad4; - return copy; + public int hashCode() { + return 31 * (int)(this.start + this.end + this.quad0 + this.quad1 + this.quad2 + this.quad3); } /** * {@inheritDoc} */ @Override - public Data query(long start, long end) { - return copy(); + public boolean equals(Object obj) { + if (!(obj instanceof QuadrantData)) + return false; + QuadrantData data = (QuadrantData) obj; + if (this.start == data.start && this.end == data.end && this.quad0 == data.quad0 + && this.quad1 == data.quad1 && this.quad2 == data.quad2 && this.quad3 == data.quad3) + { + return true; + } + return false; } /** @@ -238,29 +257,14 @@ public Data query(long start, long end) { */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); + builder.append(quad0).append(","); builder.append(quad1).append(","); builder.append(quad2).append(","); - builder.append(quad3).append(","); - builder.append(quad4); + builder.append(quad3); return builder.toString(); } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (!(obj instanceof QuadrantData)) - return false; - QuadrantData data = (QuadrantData) obj; - if (this.start == data.start && this.end == data.end && this.quad1 == data.quad1 - && this.quad2 == data.quad2 && this.quad3 == data.quad3 && this.quad4 == data.quad4) { - return true; - } - return false; - } } /** @@ -270,10 +274,6 @@ public static final class RangeMaximumData extends Data { public N maximum = null; - public RangeMaximumData(long index) { - super(index); - } - public RangeMaximumData(long start, long end) { super(start, end); } @@ -294,7 +294,35 @@ public RangeMaximumData(long start, long end, N number) { * {@inheritDoc} */ @Override - @SuppressWarnings("unchecked") + public void clear() { + super.clear(); + + maximum = null; + } + + /** + * {@inheritDoc} + */ + @Override + public Data copy() { + return new RangeMaximumData(start, end, maximum); + } + + /** + * {@inheritDoc} + */ + @Override + public Data query(long startOfQuery, long endOfQuery) { + if (endOfQuery < this.start || startOfQuery > this.end) + return null; + + return copy(); + } + + /** + * {@inheritDoc} + */ + @Override public Data combined(Data data) { RangeMaximumData q = null; if (data instanceof RangeMaximumData) { @@ -304,12 +332,6 @@ public Data combined(Data data) { return this; } - /** - * Combined for range maximum specific data. - * - * @param data - * resulted from the combination. - */ private void combined(RangeMaximumData data) { if (this.maximum == null && data.maximum == null) return; @@ -317,8 +339,28 @@ else if (this.maximum != null && data.maximum == null) return; else if (this.maximum == null && data.maximum != null) this.maximum = data.maximum; - else if (data.maximum.doubleValue() > this.maximum.doubleValue()) { - this.maximum = data.maximum; + else { + /* TODO: This is ugly */ + if (this.maximum instanceof BigDecimal || data.maximum instanceof BigDecimal) { + if (((BigDecimal)data.maximum).compareTo(((BigDecimal)this.maximum))==1) + this.maximum = data.maximum; + } else if (this.maximum instanceof BigInteger || data.maximum instanceof BigInteger) { + if (((BigInteger)data.maximum).compareTo(((BigInteger)this.maximum))==1) + this.maximum = data.maximum; + } else if (this.maximum instanceof Long || data.maximum instanceof Long) { + if (((Long)data.maximum).compareTo(((Long)this.maximum))==1) + this.maximum = data.maximum; + } else if (this.maximum instanceof Double || data.maximum instanceof Double) { + if (((Double)data.maximum).compareTo(((Double)this.maximum))==1) + this.maximum = data.maximum; + } else if (this.maximum instanceof Float || data.maximum instanceof Float) { + if (((Float)data.maximum).compareTo(((Float)this.maximum))==1) + this.maximum = data.maximum; + } else { + // Integer + if (((Integer)data.maximum).compareTo(((Integer)this.maximum))==1) + this.maximum = data.maximum; + } } } @@ -326,16 +368,22 @@ else if (data.maximum.doubleValue() > this.maximum.doubleValue()) { * {@inheritDoc} */ @Override - public Data copy() { - return new RangeMaximumData(start, end, maximum); + public int hashCode() { + return 31 * (int)(this.start + this.end + this.maximum.hashCode()); } /** * {@inheritDoc} */ @Override - public Data query(long start, long end) { - return copy(); + public boolean equals(Object obj) { + if (!(obj instanceof RangeMaximumData)) + return false; + + final RangeMaximumData data = (RangeMaximumData) obj; + if (this.start == data.start && this.end == data.end && this.maximum.equals(data.maximum)) + return true; + return false; } /** @@ -348,20 +396,6 @@ public String toString() { builder.append("maximum=").append(maximum); return builder.toString(); } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (!(obj instanceof RangeMaximumData)) - return false; - @SuppressWarnings("unchecked") - RangeMaximumData data = (RangeMaximumData) obj; - if (this.start == data.start && this.end == data.end && this.maximum.equals(data.maximum)) - return true; - return false; - } } /** @@ -371,10 +405,6 @@ public static final class RangeMinimumData extends Data { public N minimum = null; - public RangeMinimumData(long index) { - super(index); - } - public RangeMinimumData(long start, long end) { super(start, end); } @@ -405,7 +435,25 @@ public void clear() { * {@inheritDoc} */ @Override - @SuppressWarnings("unchecked") + public Data copy() { + return new RangeMinimumData(start, end, minimum); + } + + /** + * {@inheritDoc} + */ + @Override + public Data query(long startOfQuery, long endOfQuery) { + if (endOfQuery < this.start || startOfQuery > this.end) + return null; + + return copy(); + } + + /** + * {@inheritDoc} + */ + @Override public Data combined(Data data) { RangeMinimumData q = null; if (data instanceof RangeMinimumData) { @@ -415,12 +463,6 @@ public Data combined(Data data) { return this; } - /** - * Combined specific to range minimum specific data. - * - * @param data - * resulted from combination. - */ private void combined(RangeMinimumData data) { if (this.minimum == null && data.minimum == null) return; @@ -428,8 +470,28 @@ else if (this.minimum != null && data.minimum == null) return; else if (this.minimum == null && data.minimum != null) this.minimum = data.minimum; - else if (data.minimum.doubleValue() < this.minimum.doubleValue()) { - this.minimum = data.minimum; + else { + /* TODO: This is ugly */ + if (this.minimum instanceof BigDecimal || data.minimum instanceof BigDecimal) { + if (((BigDecimal)data.minimum).compareTo(((BigDecimal)this.minimum))==-1) + this.minimum = data.minimum; + } else if (this.minimum instanceof BigInteger || data.minimum instanceof BigInteger) { + if (((BigInteger)data.minimum).compareTo(((BigInteger)this.minimum))==-1) + this.minimum = data.minimum; + } else if (this.minimum instanceof Long || data.minimum instanceof Long) { + if (((Long)data.minimum).compareTo(((Long)this.minimum))==-1) + this.minimum = data.minimum; + } else if (this.minimum instanceof Double || data.minimum instanceof Double) { + if (((Double)data.minimum).compareTo(((Double)this.minimum))==-1) + this.minimum = data.minimum; + } else if (this.minimum instanceof Float || data.minimum instanceof Float) { + if (((Float)data.minimum).compareTo(((Float)this.minimum))==-1) + this.minimum = data.minimum; + } else { + // Integer + if (((Integer)data.minimum).compareTo(((Integer)this.minimum))==-1) + this.minimum = data.minimum; + } } } @@ -437,16 +499,23 @@ else if (data.minimum.doubleValue() < this.minimum.doubleValue()) { * {@inheritDoc} */ @Override - public Data copy() { - return new RangeMinimumData(start, end, minimum); + public int hashCode() { + return 31 * (int)(this.start + this.end + this.minimum.hashCode()); } /** * {@inheritDoc} */ @Override - public Data query(long start, long end) { - return copy(); + public boolean equals(Object obj) { + if (!(obj instanceof RangeMinimumData)) + return false; + + final RangeMinimumData data = (RangeMinimumData) obj; + if (this.start == data.start && this.end == data.end && this.minimum.equals(data.minimum)) + return true; + + return false; } /** @@ -459,20 +528,6 @@ public String toString() { builder.append("minimum=").append(minimum); return builder.toString(); } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (!(obj instanceof RangeMinimumData)) - return false; - @SuppressWarnings("unchecked") - RangeMinimumData data = (RangeMinimumData) obj; - if (this.start == data.start && this.end == data.end && this.minimum.equals(data.minimum)) - return true; - return false; - } } /** @@ -482,10 +537,6 @@ public static final class RangeSumData extends Data { public N sum = null; - public RangeSumData(long index) { - super(index); - } - public RangeSumData(long start, long end) { super(start, end); } @@ -516,7 +567,25 @@ public void clear() { * {@inheritDoc} */ @Override - @SuppressWarnings("unchecked") + public Data copy() { + return new RangeSumData(start, end, sum); + } + + /** + * {@inheritDoc} + */ + @Override + public Data query(long startOfQuery, long endOfQuery) { + if (endOfQuery < this.start || startOfQuery > this.end) + return null; + + return copy(); + } + + /** + * {@inheritDoc} + */ + @Override public Data combined(Data data) { RangeSumData q = null; if (data instanceof RangeSumData) { @@ -526,13 +595,6 @@ public Data combined(Data data) { return this; } - /** - * Combined specific to range sum specific data. - * - * @param data - * resulted from combination. - */ - @SuppressWarnings("unchecked") private void combined(RangeSumData data) { if (this.sum == null && data.sum == null) return; @@ -541,10 +603,27 @@ else if (this.sum != null && data.sum == null) else if (this.sum == null && data.sum != null) this.sum = data.sum; else { - Double d1 = this.sum.doubleValue(); - Double d2 = data.sum.doubleValue(); - Double r = d1 + d2; - this.sum = (N) r; + /* TODO: This is ugly and how to handle number overflow? */ + if (this.sum instanceof BigDecimal || data.sum instanceof BigDecimal) { + BigDecimal result = ((BigDecimal)this.sum).add((BigDecimal)data.sum); + this.sum = (N)result; + } else if (this.sum instanceof BigInteger || data.sum instanceof BigInteger) { + BigInteger result = ((BigInteger)this.sum).add((BigInteger)data.sum); + this.sum = (N)result; + } else if (this.sum instanceof Long || data.sum instanceof Long) { + Long result = (this.sum.longValue() + data.sum.longValue()); + this.sum = (N)result; + } else if (this.sum instanceof Double || data.sum instanceof Double) { + Double result = (this.sum.doubleValue() + data.sum.doubleValue()); + this.sum = (N)result; + } else if (this.sum instanceof Float || data.sum instanceof Float) { + Float result = (this.sum.floatValue() + data.sum.floatValue()); + this.sum = (N)result; + } else { + // Integer + Integer result = (this.sum.intValue() + data.sum.intValue()); + this.sum = (N)result; + } } } @@ -552,16 +631,23 @@ else if (this.sum == null && data.sum != null) * {@inheritDoc} */ @Override - public Data copy() { - return new RangeSumData(start, end, sum); + public int hashCode() { + return 31 * (int)(this.start + this.end + this.sum.hashCode()); } /** * {@inheritDoc} */ @Override - public Data query(long start, long end) { - return copy(); + public boolean equals(Object obj) { + if (!(obj instanceof RangeSumData)) + return false; + + final RangeSumData data = (RangeSumData) obj; + if (this.start == data.start && this.end == data.end && this.sum.equals(data.sum)) + return true; + + return false; } /** @@ -569,25 +655,11 @@ public Data query(long start, long end) { */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); builder.append("sum=").append(sum); return builder.toString(); } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (!(obj instanceof RangeSumData)) - return false; - @SuppressWarnings("unchecked") - RangeSumData data = (RangeSumData) obj; - if (this.start == data.start && this.end == data.end && this.sum.equals(data.sum)) - return true; - return false; - } } /** @@ -624,24 +696,22 @@ public IntervalData(long start, long end, O object) { /** * Interval data list which should all be unique * - * @param list + * @param set * of interval data objects */ public IntervalData(long start, long end, Set set) { super(start, end); this.set = set; + } - // Make sure they are unique - Iterator iter = set.iterator(); - while (iter.hasNext()) { - O obj1 = iter.next(); - O obj2 = null; - if (iter.hasNext()) - obj2 = iter.next(); - if (obj1.equals(obj2)) - throw new InvalidParameterException("Each interval data in the list must be unique."); - } + /** + * Get the data set in this interval + * + * @return Unmodifiable collection of data objects + */ + public Collection getData() { + return Collections.unmodifiableCollection(this.set); } /** @@ -658,7 +728,27 @@ public void clear() { * {@inheritDoc} */ @Override - @SuppressWarnings("unchecked") + public Data copy() { + final Set listCopy = new TreeSet(); + listCopy.addAll(set); + return new IntervalData(start, end, listCopy); + } + + /** + * {@inheritDoc} + */ + @Override + public Data query(long startOfQuery, long endOfQuery) { + if (endOfQuery < this.start || startOfQuery > this.end) + return null; + + return copy(); + } + + /** + * {@inheritDoc} + */ + @Override public Data combined(Data data) { IntervalData q = null; if (data instanceof IntervalData) { @@ -686,34 +776,8 @@ private void combined(IntervalData data) { * {@inheritDoc} */ @Override - public Data copy() { - Set listCopy = new TreeSet(); - listCopy.addAll(set); - return new IntervalData(start, end, listCopy); - } - - /** - * {@inheritDoc} - */ - @Override - public Data query(long start, long end) { - if (end < this.start || start > this.end) { - // Ignore - } else { - return copy(); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(super.toString()).append(" "); - builder.append("set=").append(set); - return builder.toString(); + public int hashCode() { + return 31 * (int)(this.start + this.end + this.set.size()); } /** @@ -723,8 +787,8 @@ public String toString() { public boolean equals(Object obj) { if (!(obj instanceof IntervalData)) return false; - @SuppressWarnings("unchecked") - IntervalData data = (IntervalData) obj; + + final IntervalData data = (IntervalData) obj; if (this.start == data.start && this.end == data.end) { if (this.set.size() != data.set.size()) return false; @@ -736,6 +800,17 @@ public boolean equals(Object obj) { } return false; } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(super.toString()).append(" "); + builder.append("set=").append(set); + return builder.toString(); + } } } @@ -759,13 +834,13 @@ public Segment(int minLength) { /** * Query for data in range. * - * @param start + * @param startOfQuery * of the range to query for. - * @param end + * @param endOfQuery * of range to query for. * @return Data in the range. */ - public abstract D query(long start, long end); + public abstract D query(long startOfQuery, long endOfQuery); protected boolean hasChildren() { return (segments != null); @@ -784,7 +859,7 @@ protected Segment getRightChild() { */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append(start).append("->"); builder.append(end).append(" "); builder.append("Length=").append(length).append(" "); @@ -814,20 +889,20 @@ public static String getString(SegmentTree tree) } private static String getString(Segment segment, String prefix, boolean isTail) { - StringBuilder builder = new StringBuilder(); - + final StringBuilder builder = new StringBuilder(); builder.append(prefix + (isTail ? "└── " : "├── ") + segment.toString() + "\n"); - List> children = new ArrayList>(); + + final List> children = new ArrayList>(); if (segment.segments != null) { for (Segment c : segment.segments) children.add(c); } - for (int i = 0; i < children.size() - 1; i++) { + + for (int i = 0; i < children.size() - 1; i++) builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false)); - } - if (children.size() > 1) { + + if (children.size() > 1) builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); - } return builder.toString(); } @@ -847,9 +922,7 @@ public FlatSegmentTree(List data) { this(data, 1); } - @SuppressWarnings("unchecked") public FlatSegmentTree(List data, int minLength) { - if (data.size() <= 0) throw new InvalidParameterException("Segments list is empty."); @@ -866,31 +939,31 @@ public FlatSegmentTree(List data, int minLength) { } // Check for gaps - List> segments = new ArrayList>(); + final List> segments = new ArrayList>(); for (int i = 0; i < data.size(); i++) { if (i < data.size() - 1) { - Data d1 = data.get(i); - NonOverlappingSegment s1 = new NonOverlappingSegment(minLength, d1.start, d1.end, (D) d1); + final Data d1 = data.get(i); + final NonOverlappingSegment s1 = new NonOverlappingSegment(minLength, d1.start, d1.end, (D) d1); segments.add(s1); - Data d2 = data.get(i + 1); + final Data d2 = data.get(i + 1); if (d2.start - d1.end > 1) { - Data d3 = d1.copy(); + final Data d3 = d1.copy(); d3.clear(); d3.start = d1.end + 1; d3.end = d2.start - 1; - NonOverlappingSegment s3 = new NonOverlappingSegment(minLength, d3.start, d3.end, (D) d3); + final NonOverlappingSegment s3 = new NonOverlappingSegment(minLength, d3.start, d3.end, (D) d3); segments.add(s3); } } else { - Data d1 = data.get(i); - NonOverlappingSegment s1 = new NonOverlappingSegment(minLength, d1.start, d1.end, (D) d1); + final Data d1 = data.get(i); + final NonOverlappingSegment s1 = new NonOverlappingSegment(minLength, d1.start, d1.end, (D) d1); segments.add(s1); } } - long start = segments.get(0).start; - long end = segments.get(segments.size() - 1).end; - int length = (int) (end - start) + 1; + final long start = segments.get(0).start; + final long end = segments.get(segments.size() - 1).end; + final int length = (int) (end - start) + 1; root = NonOverlappingSegment.createFromList(minLength, segments, start, length); } @@ -906,16 +979,18 @@ public D query(long index) { * {@inheritDoc} */ @Override - public D query(long start, long end) { + public D query(long startOfQuery, long endOfQuery) { if (root == null) return null; - if (start < root.start) - start = root.start; - if (end > root.end) - end = root.end; + long s = startOfQuery; + long e = endOfQuery; + if (s < root.start) + s = root.start; + if (e > root.end) + e = root.end; - return root.query(start, end); + return root.query(s, e); } /** @@ -933,9 +1008,9 @@ public NonOverlappingSegment(int minLength, D data) { this(minLength, data.start, data.end, data); } - @SuppressWarnings("unchecked") public NonOverlappingSegment(int minLength, long start, long end, D data) { super(minLength); + this.start = start; this.end = end; this.length = ((int) (end - start)) + 1; @@ -944,10 +1019,8 @@ public NonOverlappingSegment(int minLength, long start, long end, D data) { this.data = ((D) data.copy()); } - @SuppressWarnings("unchecked") - protected static Segment createFromList(int minLength, - List> segments, long start, int length) { - NonOverlappingSegment segment = new NonOverlappingSegment(minLength); + protected static Segment createFromList(int minLength, List> segments, long start, int length) { + final NonOverlappingSegment segment = new NonOverlappingSegment(minLength); segment.start = start; segment.end = start + (length - 1); segment.length = length; @@ -956,37 +1029,34 @@ protected static Segment createFromList(int minLength, if (segment.data == null) segment.data = ((D) s.data.copy()); else - segment.data.combined(s.data); // Update our data to - // reflect all - // children's data + segment.data.combined(s.data); // Update our data to reflect all children's data } - // If segment is greater or equal to two, split data into - // children + // If segment is greater or equal to two, split data into children if (segment.length >= 2 && segment.length >= minLength) { segment.half = segment.length / 2; - List> s1 = new ArrayList>(); - List> s2 = new ArrayList>(); + + final List> s1 = new ArrayList>(); + final List> s2 = new ArrayList>(); for (int i = 0; i < segments.size(); i++) { - NonOverlappingSegment s = segments.get(i); - long middle = segment.start + segment.half; + final NonOverlappingSegment s = segments.get(i); + final long middle = segment.start + segment.half; if (s.end < middle) { s1.add(s); } else if (s.start >= middle) { s2.add(s); } else { // Need to split across middle - NonOverlappingSegment ss1 = new NonOverlappingSegment(minLength, s.start, middle - 1, - s.data); + final NonOverlappingSegment ss1 = new NonOverlappingSegment(minLength, s.start, middle - 1, s.data); s1.add(ss1); - NonOverlappingSegment ss2 = new NonOverlappingSegment(minLength, middle, s.end, - s.data); + + final NonOverlappingSegment ss2 = new NonOverlappingSegment(minLength, middle, s.end, s.data); s2.add(ss2); } } - Segment sub1 = createFromList(minLength, s1, segment.start, segment.half); - Segment sub2 = createFromList(minLength, s2, segment.start + segment.half, segment.length - - segment.half); + + final Segment sub1 = createFromList(minLength, s1, segment.start, segment.half); + final Segment sub2 = createFromList(minLength, s2, segment.start + segment.half, segment.length - segment.half); segment.segments = new Segment[] { sub1, sub2 }; } else if (segment.length <= minLength) { for (Segment s : segments) { @@ -1001,51 +1071,56 @@ protected static Segment createFromList(int minLength, * {@inheritDoc} */ @Override - @SuppressWarnings("unchecked") - public D query(long start, long end) { - if (start == this.start && end == this.end) { + public D query(long startOfQuery, long endOfQuery) { + if (startOfQuery == this.start && endOfQuery == this.end) { if (this.data == null) return null; - D dataToReturn = ((D) this.data.query(start, end)); + final D dataToReturn = ((D) this.data.query(startOfQuery, endOfQuery)); return dataToReturn; - } else if (!this.hasChildren()) { - if (end < this.start || start > this.end) { + } + + if (!this.hasChildren()) { + if (endOfQuery < this.start || startOfQuery > this.end) { // Ignore } else { D dataToReturn = null; if (this.set.size() == 0) return dataToReturn; for (Segment s : this.set) { - if (s.start >= start && s.end <= end) { + if (s.start >= startOfQuery && s.end <= endOfQuery) { if (dataToReturn == null) - dataToReturn = (D) s.data.query(start, end); + dataToReturn = (D) s.data.query(startOfQuery, endOfQuery); else dataToReturn.combined(s.data); - } else if (s.start <= start && s.end >= end) { + } else if (s.start <= startOfQuery && s.end >= endOfQuery) { if (dataToReturn == null) - dataToReturn = (D) s.data.query(start, end); + dataToReturn = (D) s.data.query(startOfQuery, endOfQuery); else dataToReturn.combined(s.data); } } return dataToReturn; } - } else if (this.hasChildren()) { - if (start <= this.getLeftChild().end && end > this.getLeftChild().end) { - Data q1 = this.getLeftChild().query(start, getLeftChild().end); - Data q2 = this.getRightChild().query(getRightChild().start, end); + } + + if (this.hasChildren()) { + if (startOfQuery <= this.getLeftChild().end && endOfQuery > this.getLeftChild().end) { + final Data q1 = this.getLeftChild().query(startOfQuery, getLeftChild().end); + final Data q2 = this.getRightChild().query(getRightChild().start, endOfQuery); if (q1 == null && q2 == null) return null; if (q1 != null && q2 == null) return (D) q1; if (q1 == null && q2 != null) return (D) q2; - return ((D) q1.combined(q2)); - } else if (start <= this.getLeftChild().end && end <= this.getLeftChild().end) { - return this.getLeftChild().query(start, end); + if (q1 != null && q2 != null) + return ((D) q1.combined(q2)); + } else if (startOfQuery <= this.getLeftChild().end && endOfQuery <= this.getLeftChild().end) { + return this.getLeftChild().query(startOfQuery, endOfQuery); } - return this.getRightChild().query(start, end); + return this.getRightChild().query(startOfQuery, endOfQuery); } + return null; } @@ -1054,7 +1129,7 @@ public D query(long start, long end) { */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append(super.toString()).append(" "); builder.append("Set=").append(set); return builder.toString(); @@ -1071,73 +1146,82 @@ public String toString() { */ public static final class DynamicSegmentTree extends SegmentTree { + private static final Comparator> START_COMPARATOR = new Comparator>() { + /** + * {@inheritDoc} + */ + @Override + public int compare(OverlappingSegment arg0, OverlappingSegment arg1) { + if (arg0.start < arg1.start) + return -1; + if (arg1.start < arg0.start) + return 1; + return 0; + } + }; + + private static final Comparator> END_COMPARATOR = new Comparator>() { + /** + * {@inheritDoc} + */ + @Override + public int compare(OverlappingSegment arg0, OverlappingSegment arg1) { + if (arg0.end < arg1.end) + return -1; + if (arg1.end < arg0.end) + return 1; + return 0; + } + }; + public DynamicSegmentTree(List data) { this(data, 1); } - @SuppressWarnings("unchecked") public DynamicSegmentTree(List data, int minLength) { if (data.size() <= 0) return; // Check for gaps - List> segments = new ArrayList>(); + final List> segments = new ArrayList>(); for (int i = 0; i < data.size(); i++) { if (i < data.size() - 1) { - Data d1 = data.get(i); - OverlappingSegment s1 = new OverlappingSegment(minLength, d1.start, d1.end, (D) d1); + final Data d1 = data.get(i); + final OverlappingSegment s1 = new OverlappingSegment(minLength, d1.start, d1.end, (D) d1); segments.add(s1); - Data d2 = data.get(i + 1); + + final Data d2 = data.get(i + 1); if (d2.start - d1.end > 1) { - Data d3 = d1.copy(); + final Data d3 = d1.copy(); d3.clear(); d3.start = d1.end + 1; d3.end = d2.start - 1; - OverlappingSegment s3 = new OverlappingSegment(minLength, d3.start, d3.end, (D) d3); + + final OverlappingSegment s3 = new OverlappingSegment(minLength, d3.start, d3.end, (D) d3); segments.add(s3); } } else { - Data d1 = data.get(i); - OverlappingSegment s1 = new OverlappingSegment(minLength, d1.start, d1.end, (D) d1); + final Data d1 = data.get(i); + final OverlappingSegment s1 = new OverlappingSegment(minLength, d1.start, d1.end, (D) d1); segments.add(s1); } } // First start first - Collections.sort(segments, new Comparator>() { - - @Override - public int compare(OverlappingSegment arg0, OverlappingSegment arg1) { - if (arg0.start < arg1.start) - return -1; - if (arg1.start < arg0.start) - return 1; - return 0; - } - }); - OverlappingSegment startNode = segments.get(0); - long start = startNode.start - 1; - OverlappingSegment s1 = new OverlappingSegment(minLength, start, startNode.start, null); + Collections.sort(segments, START_COMPARATOR); + final OverlappingSegment startNode = segments.get(0); + final long start = startNode.start - 1; + final OverlappingSegment s1 = new OverlappingSegment(minLength, start, startNode.start, null); segments.add(0, s1); // Last end last - Collections.sort(segments, new Comparator>() { - - @Override - public int compare(OverlappingSegment arg0, OverlappingSegment arg1) { - if (arg0.end < arg1.end) - return -1; - if (arg1.end < arg0.end) - return 1; - return 0; - } - }); - OverlappingSegment endNode = segments.get(segments.size() - 1); - long end = endNode.end + 1; - OverlappingSegment s2 = new OverlappingSegment(minLength, endNode.end, end, null); + Collections.sort(segments, END_COMPARATOR); + final OverlappingSegment endNode = segments.get(segments.size() - 1); + final long end = endNode.end + 1; + final OverlappingSegment s2 = new OverlappingSegment(minLength, endNode.end, end, null); segments.add(s2); - int length = (int) (end - start) + 1; + final int length = (int) (end - start) + 1; root = OverlappingSegment.createFromList(minLength, segments, start, length); } @@ -1153,16 +1237,21 @@ public D query(long index) { * {@inheritDoc} */ @Override - public D query(long start, long end) { + public D query(long startOfQuery, long endOfQuery) { if (root == null) return null; - if (start < root.start) - start = root.start; - if (end > root.end) - end = root.end; - - D result = root.query(start, end); + long s = startOfQuery; + long e = endOfQuery; + if (s < root.start) + s = root.start; + if (e > root.end) + e = root.end; + + final D result = root.query(s, e); + // reset the start and end, it can change during the query + result.start = startOfQuery; + result.end = endOfQuery; return result; } @@ -1178,9 +1267,9 @@ public OverlappingSegment(int minLength) { super(minLength); } - @SuppressWarnings("unchecked") public OverlappingSegment(int minLength, long start, long end, D data) { super(minLength); + this.start = start; this.end = end; this.length = ((int) (end - start)) + 1; @@ -1189,10 +1278,8 @@ public OverlappingSegment(int minLength, long start, long end, D data) { this.data = ((D) data.copy()); } - @SuppressWarnings("unchecked") - protected static Segment createFromList(int minLength, - List> segments, long start, int length) { - OverlappingSegment segment = new OverlappingSegment(minLength); + protected static Segment createFromList(int minLength, List> segments, long start, int length) { + final OverlappingSegment segment = new OverlappingSegment(minLength); segment.start = start; segment.end = start + (length - 1); segment.length = length; @@ -1209,44 +1296,40 @@ protected static Segment createFromList(int minLength, if (segment.data == null) segment.data = ((D) s.data.copy()); else - segment.data.combined(s.data); // Update our data - // to reflect all - // children's data + segment.data.combined(s.data); // Update our data to reflect all children's data } else if (!segment.hasChildren() && s.start >= segment.start && s.end <= segment.end) { if (segment.data == null) segment.data = ((D) s.data.copy()); else - segment.data.combined(s.data); // Update our data - // to reflect all - // children's data + segment.data.combined(s.data); // Update our data to reflect all children's data } } - // If segment is greater or equal to two, split data into - // children + // If segment is greater or equal to two, split data into children if (segment.length >= 2 && segment.length >= minLength) { segment.half = segment.length / 2; - List> s1 = new ArrayList>(); - List> s2 = new ArrayList>(); + + final List> s1 = new ArrayList>(); + final List> s2 = new ArrayList>(); for (int i = 0; i < segments.size(); i++) { - OverlappingSegment s = segments.get(i); - long middle = segment.start + segment.half; + final OverlappingSegment s = segments.get(i); + final long middle = segment.start + segment.half; if (s.end < middle) { s1.add(s); } else if (s.start >= middle) { s2.add(s); } else { // Need to split across middle - OverlappingSegment ss1 = new OverlappingSegment(minLength, s.start, middle - 1, - s.data); + final OverlappingSegment ss1 = new OverlappingSegment(minLength, s.start, middle - 1, s.data); s1.add(ss1); - OverlappingSegment ss2 = new OverlappingSegment(minLength, middle, s.end, s.data); + + final OverlappingSegment ss2 = new OverlappingSegment(minLength, middle, s.end, s.data); s2.add(ss2); } } - Segment sub1 = createFromList(minLength, s1, segment.start, segment.half); - Segment sub2 = createFromList(minLength, s2, segment.start + segment.half, segment.length - - segment.half); + + final Segment sub1 = createFromList(minLength, s1, segment.start, segment.half); + final Segment sub2 = createFromList(minLength, s2, segment.start + segment.half, segment.length - segment.half); segment.segments = new Segment[] { sub1, sub2 }; } @@ -1257,14 +1340,13 @@ protected static Segment createFromList(int minLength, * {@inheritDoc} */ @Override - @SuppressWarnings("unchecked") - public D query(long start, long end) { + public D query(long startOfQuery, long endOfQuery) { D result = null; // Use the range data to make range queries faster - if (start == this.start && end == this.end) { + if (startOfQuery == this.start && endOfQuery == this.end) { for (Segment s : this.range) { - D temp = (D) s.data.query(start, end); + final D temp = (D) s.data.query(startOfQuery, endOfQuery); if (temp != null) { if (result == null) result = (D) temp.copy(); @@ -1273,14 +1355,14 @@ public D query(long start, long end) { } } } else if (!this.hasChildren()) { - if (end < this.start || start > this.end) { + if (endOfQuery < this.start || startOfQuery > this.end) { // Ignore } else { for (Segment s : this.range) { - if (end < s.start || start > s.end) { + if (endOfQuery < s.start || startOfQuery > s.end) { // Ignore } else { - D temp = (D) s.data.query(start, end); + final D temp = (D) s.data.query(startOfQuery, endOfQuery); if (temp != null) { if (result == null) result = (D) temp.copy(); @@ -1291,21 +1373,21 @@ public D query(long start, long end) { } } } else { - long middle = this.start + this.half; + final long middle = this.start + this.half; D temp = null; - if (start < middle && end >= middle) { - temp = this.getLeftChild().query(start, middle - 1); - D temp2 = this.getRightChild().query(middle, end); + if (startOfQuery < middle && endOfQuery >= middle) { + temp = this.getLeftChild().query(startOfQuery, middle - 1); + D temp2 = this.getRightChild().query(middle, endOfQuery); if (temp2 != null) { if (temp == null) temp = (D) temp2.copy(); else temp.combined(temp2); } - } else if (end < middle) { - temp = this.getLeftChild().query(start, end); - } else if (start >= middle) { - temp = this.getRightChild().query(start, end); + } else if (endOfQuery < middle) { + temp = this.getLeftChild().query(startOfQuery, endOfQuery); + } else if (startOfQuery >= middle) { + temp = this.getRightChild().query(startOfQuery, endOfQuery); } if (temp != null) result = (D) temp.copy(); diff --git a/src/com/jwetherell/algorithms/data_structures/SkipList.java b/src/com/jwetherell/algorithms/data_structures/SkipList.java index 14ed90ec..29b61f70 100644 --- a/src/com/jwetherell/algorithms/data_structures/SkipList.java +++ b/src/com/jwetherell/algorithms/data_structures/SkipList.java @@ -2,14 +2,16 @@ import java.util.Random; +import com.jwetherell.algorithms.data_structures.interfaces.ISet; + /** * Skip List. A skip list is a data structure for storing a sorted list of items * using a hierarchy of linked lists that connect increasingly sparse * subsequences of the items. These auxiliary lists allow item lookup with * efficiency comparable to balanced binary search trees. - * - * http://en.wikipedia.org/wiki/Skip_list - * + *

+ * @see Skip List (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") @@ -280,10 +282,8 @@ public boolean validate() { Node node = prev.getNext(i); while (node != null) { // The list should be ordered - if (node.data.compareTo(prev.data) < 1) { - System.err.println("List is not in order."); + if (node.data.compareTo(prev.data) < 1) return false; - } prev = node; node = prev.getNext(i); } @@ -373,9 +373,9 @@ protected Node getNext(int idx) { public String toString() { StringBuilder builder = new StringBuilder(); builder.append("data=").append(data); - int size = next.length; if (next!=null) { builder.append("\n").append("next=["); + int size = next.length; for (int i=0; i n = next[i]; if (n!=null) builder.append(n.data); diff --git a/src/com/jwetherell/algorithms/data_structures/SkipListMap.java b/src/com/jwetherell/algorithms/data_structures/SkipListMap.java index 868531c9..d7c77eb9 100644 --- a/src/com/jwetherell/algorithms/data_structures/SkipListMap.java +++ b/src/com/jwetherell/algorithms/data_structures/SkipListMap.java @@ -1,16 +1,17 @@ package com.jwetherell.algorithms.data_structures; import com.jwetherell.algorithms.data_structures.SkipList.Node; +import com.jwetherell.algorithms.data_structures.interfaces.IMap; /** * A set used to store key->values pairs, this is an implementation of an * associative array. - * + *

* This implementation is a composition of a Skip List as the backing structure. - * - * http://en.wikipedia.org/wiki/Skip_list - * http://en.wikipedia.org/wiki/Associative_array - * + *

+ * @see Skip List (Wikipedia) + * @see Associative Array (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") @@ -69,6 +70,8 @@ public V remove(K key) { if (node instanceof SkipListMapNode) { SkipListMapNode treeMapNode = (SkipListMapNode) node; value = treeMapNode.value; + treeMapNode.data = null; + treeMapNode.value = null; } return value; } diff --git a/src/com/jwetherell/algorithms/data_structures/SplayTree.java b/src/com/jwetherell/algorithms/data_structures/SplayTree.java index 33bf183f..d6e279db 100644 --- a/src/com/jwetherell/algorithms/data_structures/SplayTree.java +++ b/src/com/jwetherell/algorithms/data_structures/SplayTree.java @@ -3,9 +3,9 @@ /** * A splay tree is a self-adjusting binary search tree (BST) with the additional * property that recently accessed elements are quick to access again. - * - * http://en.wikipedia.org/wiki/Splay_tree - * + *

+ * @see Splay Tree (Wikipedia) + *
* @author Justin Wetherell */ public class SplayTree> extends BinarySearchTree { @@ -32,13 +32,11 @@ protected Node addValue(T id) { @Override protected Node removeValue(T value) { Node nodeToRemove = super.removeValue(value); - if (nodeToRemove != null) { - if (nodeToRemove.parent != null) { - Node nodeParent = nodeToRemove.parent; - // Splay the parent node to the root position - while (nodeParent.parent != null) { - this.splay(nodeParent); - } + if (nodeToRemove != null && nodeToRemove.parent != null) { + Node nodeParent = nodeToRemove.parent; + // Splay the parent node to the root position + while (nodeParent.parent != null) { + this.splay(nodeParent); } } return nodeToRemove; @@ -69,27 +67,33 @@ public boolean contains(T value) { private void splay(Node node) { Node parent = node.parent; Node grandParent = (parent != null) ? parent.parent : null; - if (parent == root) { + if (parent != null && parent == root) { + grandParent = parent.parent; // Zig step root = node; node.parent = null; - if (node == parent.lesser) { - parent.lesser = node.greater; - if (node.greater != null) - node.greater.parent = parent; + if (parent!=null) { + if (node == parent.lesser) { + parent.lesser = node.greater; + if (node.greater != null) + node.greater.parent = parent; - node.greater = parent; - parent.parent = node; - } else { - parent.greater = node.lesser; - if (node.lesser != null) - node.lesser.parent = parent; + node.greater = parent; + parent.parent = node; + } else { + parent.greater = node.lesser; + if (node.lesser != null) + node.lesser.parent = parent; - node.lesser = parent; - parent.parent = node; + node.lesser = parent; + parent.parent = node; + } } - } else if (parent != null && grandParent != null) { + return; + } + + if (parent != null && grandParent != null) { Node greatGrandParent = grandParent.parent; if (greatGrandParent != null && greatGrandParent.lesser == grandParent) { greatGrandParent.lesser = node; @@ -104,7 +108,7 @@ private void splay(Node node) { } if ((node == parent.lesser && parent == grandParent.lesser) - || (node == parent.greater && parent == grandParent.greater)) { + || (node == parent.greater && parent == grandParent.greater)) { // Zig-zig step if (node == parent.lesser) { Node nodeGreater = node.greater; @@ -139,44 +143,46 @@ private void splay(Node node) { if (parentLesser != null) parentLesser.parent = grandParent; } - } else { - // Zig-zag step - if (node == parent.lesser) { - Node nodeLesser = node.greater; - Node nodeGreater = node.lesser; + return; + } - node.greater = parent; - parent.parent = node; + // Zig-zag step + if (node == parent.lesser) { + Node nodeLesser = node.greater; + Node nodeGreater = node.lesser; - node.lesser = grandParent; - grandParent.parent = node; + node.greater = parent; + parent.parent = node; - parent.lesser = nodeLesser; - if (nodeLesser != null) - nodeLesser.parent = parent; + node.lesser = grandParent; + grandParent.parent = node; - grandParent.greater = nodeGreater; - if (nodeGreater != null) - nodeGreater.parent = grandParent; - } else { - Node nodeLesser = node.lesser; - Node nodeGreater = node.greater; + parent.lesser = nodeLesser; + if (nodeLesser != null) + nodeLesser.parent = parent; - node.lesser = parent; - parent.parent = node; + grandParent.greater = nodeGreater; + if (nodeGreater != null) + nodeGreater.parent = grandParent; + return; + } - node.greater = grandParent; - grandParent.parent = node; + Node nodeLesser = node.lesser; + Node nodeGreater = node.greater; - parent.greater = nodeLesser; - if (nodeLesser != null) - nodeLesser.parent = parent; + node.lesser = parent; + parent.parent = node; - grandParent.lesser = nodeGreater; - if (nodeGreater != null) - nodeGreater.parent = grandParent; - } - } + node.greater = grandParent; + grandParent.parent = node; + + parent.greater = nodeLesser; + if (nodeLesser != null) + nodeLesser.parent = parent; + + grandParent.lesser = nodeGreater; + if (nodeGreater != null) + nodeGreater.parent = grandParent; } } } diff --git a/src/com/jwetherell/algorithms/data_structures/Stack.java b/src/com/jwetherell/algorithms/data_structures/Stack.java index 5fcc0089..2ad749c2 100644 --- a/src/com/jwetherell/algorithms/data_structures/Stack.java +++ b/src/com/jwetherell/algorithms/data_structures/Stack.java @@ -2,17 +2,27 @@ import java.util.Arrays; +import com.jwetherell.algorithms.data_structures.interfaces.IStack; + +/** + * In computer science, a stack is an abstract data type that serves as a collection of elements, with two principal operations: push, which adds an element to the collection, and pop, which removes + * the most recently added element that was not yet removed. + *

+ * @see Stack (Wikipedia) + *
+ * @author Justin Wetherell + */ @SuppressWarnings("unchecked") public interface Stack extends IStack { /** * This stack implementation is backed by an array. - * + *

* @author Justin Wetherell */ public static class ArrayStack implements Stack { - private static final int MINIMUM_SIZE = 10; + private static final int MINIMUM_SIZE = 1024; private T[] array = (T[]) new Object[MINIMUM_SIZE]; private int size = 0; @@ -22,10 +32,8 @@ public static class ArrayStack implements Stack { */ @Override public boolean push(T value) { - int growSize = this.size; - if (growSize >= array.length) { - array = Arrays.copyOf(array, (growSize + (growSize>>1))); - } + if (size >= array.length) + grow(); array[size++] = value; return true; } @@ -40,10 +48,9 @@ public T pop() { T t = array[--size]; array[size] = null; - int shrinkSize = size; - if (size >= MINIMUM_SIZE && size < (shrinkSize + (shrinkSize<<1))) { - System.arraycopy(array, 0, array, 0, size); - } + int shrinkSize = array.length>>1; + if (shrinkSize >= MINIMUM_SIZE && size < shrinkSize) + shrink(); return t; } @@ -91,13 +98,25 @@ private boolean remove(int index) { } array[size] = null; - if (size >= MINIMUM_SIZE && size < array.length / 2) { - System.arraycopy(array, 0, array, 0, size); - } + int shrinkSize = array.length>>1; + if (shrinkSize >= MINIMUM_SIZE && size < shrinkSize) + shrink(); return true; } + // Grow the array by 50% + private void grow() { + int growSize = size + (size<<1); + array = Arrays.copyOf(array, growSize); + } + + // Shrink the array by 50% + private void shrink() { + int shrinkSize = array.length>>1; + array = Arrays.copyOf(array, shrinkSize); + } + /** * {@inheritDoc} */ @@ -176,7 +195,7 @@ public String toString() { /** * This stack implementation is backed by a linked list. - * + *

* @author Justin Wetherell */ public static class LinkedStack implements Stack { diff --git a/src/com/jwetherell/algorithms/data_structures/SuffixArray.java b/src/com/jwetherell/algorithms/data_structures/SuffixArray.java new file mode 100644 index 00000000..640754f0 --- /dev/null +++ b/src/com/jwetherell/algorithms/data_structures/SuffixArray.java @@ -0,0 +1,179 @@ +package com.jwetherell.algorithms.data_structures; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +/** + * In computer science, a suffix array is a sorted array of all suffixes of a string. + * It is a data structure used, among others, in full text indices, data compression + * algorithms and within the field of bibliometrics. + *

+ * @see Suffix Array (Wikipedia) + *

+ * NOTE: This implementation returns starting indexes instead of full suffixes + *
+ * @author Jakub Szarawarski + * @author Justin Wetherell + */ +public class SuffixArray { + + private static final StringBuilder STRING_BUILDER = new StringBuilder(); + private static final char DEFAULT_END_SEQ_CHAR = '$'; + + private final char endSeqChar; + + private String string; + private ArrayList suffixArray; + private ArrayList KMRarray; + + public SuffixArray(CharSequence sequence) { + this(sequence, DEFAULT_END_SEQ_CHAR); + } + + public SuffixArray(CharSequence sequence, char endChar) { + endSeqChar = endChar; + string = buildStringWithEndChar(sequence); + } + + public ArrayList getSuffixArray() { + if (suffixArray == null) + KMRalgorithm(); + return suffixArray; + } + + /** + * @return inverted suffix array + */ + public ArrayList getKMRarray() { + if (KMRarray == null) + KMRalgorithm(); + return KMRarray; + } + + public String getString(){ + return string; + } + + /** + * Creates suffix array using KMR algorithm with O(n log^2 n) complexity. + * + * For radius r: + * KMR[i] == k, + * when string[i..i+r-1] is kth r-letter substring of string sorted lexicographically + * KMR is counted for radius = 1,2,4,8 ... + * KMR for radius bigger than string length is the inverted suffix array + */ + private void KMRalgorithm() { + final int length = string.length(); + + ArrayList KMRinvertedList = new ArrayList(); + ArrayList KMR = getBasicKMR(length); + + int radius = 1; + while (radius < length) { + KMRinvertedList = getKMRinvertedList(KMR, radius, length); + KMR = getKMR(KMRinvertedList, length); + radius *= 2; + } + + KMRarray = new ArrayList(KMR.subList(0, length)); + suffixArray = new ArrayList(); + for (KMRsWithIndex kmr : KMRinvertedList) { + suffixArray.add(new Integer(kmr.index)); + } + } + + /** + * Creates KMR array for new radius from nearly inverted array. + * Elements from inverted array need to be grouped by substring tey represent. + * + * @param KMRinvertedList indexes are nearly inverted KMR array + * @param length string length + * @return KMR array for new radius + */ + private ArrayList getKMR(ArrayList KMRinvertedList, int length) { + final ArrayList KMR = new ArrayList(length*2); + for (int i=0; i<2*length; i++) + KMR.add(new Integer(-1)); + + int counter = 0; + for (int i=0; i0 && substringsAreEqual(KMRinvertedList, i)) + counter++; + KMR.set(KMRinvertedList.get(i).index, new Integer(counter)); + } + + return KMR; + } + + private boolean substringsAreEqual(ArrayList KMRinvertedList, int i) { + return (KMRinvertedList.get(i-1).beginKMR.equals(KMRinvertedList.get(i).beginKMR) == false) || + (KMRinvertedList.get(i-1).endKMR.equals(KMRinvertedList.get(i).endKMR) == false); + } + + /** + * helper method to create KMR array for radius = radius from KMR array for radius = radius/2 + * + * @param KMR KMR array for radius = radius/2 + * @param radius new radius + * @param length string length + * @return list of KMRsWithIndex which indexes are nearly inverted KMR array + */ + private ArrayList getKMRinvertedList(ArrayList KMR, int radius, int length) { + final ArrayList KMRinvertedList = new ArrayList(); + for (int i=0; i() { + @Override + public int compare(KMRsWithIndex A, KMRsWithIndex B) { + if (A.beginKMR.equals(B.beginKMR) == false) + return A.beginKMR.compareTo(B.beginKMR); + if (A.endKMR.equals(B.endKMR) == false) + return A.endKMR.compareTo(B.endKMR); + return A.index.compareTo(B.index); + } + } + ); + return KMRinvertedList; + } + + /** + * KMR array for radius=1, instead of initial natural numbers ascii codes are used + * + * @param length length of string + * @return pseudo KMR array for radius=1 + */ + private ArrayList getBasicKMR(int length) { + final ArrayList result = new ArrayList(length*2); + final char[] characters = string.toCharArray(); + for (int i=0; i + * @see Suffix Tree (Wikipedia) + *
* @author Justin Wetherell */ -public class SuffixTree { +public class SuffixTree implements ISuffixTree { private static final char DEFAULT_END_SEQ_CHAR = '$'; - private String string = null; - private char[] characters = null; + private final char endSeqChar; private Map linksMap = new HashMap(); private Map> edgeMap = new TreeMap>(); @@ -32,7 +33,8 @@ public class SuffixTree { private int firstCharIndex = 0; private int lastCharIndex = -1; - private char END_SEQ_CHAR = DEFAULT_END_SEQ_CHAR; + private String string; + private char[] characters; /** * Create suffix tree with sequence and default end sequence. @@ -53,19 +55,20 @@ public SuffixTree(C seq) { * which defines the end of a sequence. */ public SuffixTree(C seq, char endSeq) { - END_SEQ_CHAR = endSeq; + endSeqChar = endSeq; StringBuilder builder = new StringBuilder(seq); - if (builder.indexOf(String.valueOf(seq)) >= 0) - builder.append(END_SEQ_CHAR); + if (builder.indexOf(String.valueOf(endSeqChar)) < 0) + builder.append(endSeqChar); string = builder.toString(); int length = string.length(); characters = new char[length]; for (int i = 0; i < length; i++) { - characters[i] = string.charAt(i); + char c = string.charAt(i); + characters[i] = c; } - for (int i = 0; i < length; i++) { - addPrefix(i); + for (int j = 0; j < length; j++) { + addPrefix(j); } } @@ -76,6 +79,7 @@ public SuffixTree(C seq, char endSeq) { * sub-sequence to locate in the tree. * @return True if the sub-sequence exist in the tree. */ + @Override public boolean doesSubStringExist(C sub) { char[] chars = new char[sub.length()]; for (int i = 0; i < sub.length(); i++) { @@ -95,6 +99,7 @@ public boolean doesSubStringExist(C sub) { * * @return set of suffixes in the tree. */ + @Override public Set getSuffixes() { Set set = getSuffixes(0); return set; @@ -119,14 +124,14 @@ private Set getSuffixes(int start) { String s = (string.substring(e.firstCharIndex, e.lastCharIndex + 1)); Link n = linksMap.get(e.endNode); if (n == null) { - int index = s.indexOf(END_SEQ_CHAR); + int index = s.indexOf(endSeqChar); if (index >= 0) s = s.substring(0, index); set.add(s); } else { Set set2 = getSuffixes(e.endNode); for (String s2 : set2) { - int index = s2.indexOf(END_SEQ_CHAR); + int index = s2.indexOf(endSeqChar); if (index >= 0) s2 = s2.substring(0, index); set.add(s + s2); @@ -144,7 +149,7 @@ private Set getSuffixes(int start) { public String getEdgesTable() { StringBuilder builder = new StringBuilder(); if (edgeMap.size() > 0) { - int lastCharIndex = characters.length; + int charsLength = characters.length; builder.append("Edge\tStart\tEnd\tSuf\tfirst\tlast\tString\n"); for (int key : edgeMap.keySet()) { Edge e = edgeMap.get(key); @@ -153,7 +158,7 @@ public String getEdgesTable() { builder.append("\t" + e.startNode + "\t" + e.endNode + "\t" + suffix + "\t" + e.firstCharIndex + "\t" + e.lastCharIndex + "\t"); int begin = e.firstCharIndex; - int end = (lastCharIndex < e.lastCharIndex) ? lastCharIndex : e.lastCharIndex; + int end = (charsLength < e.lastCharIndex) ? charsLength : e.lastCharIndex; builder.append(string.substring(begin, end + 1)); builder.append("\n"); } @@ -218,7 +223,7 @@ private void addPrefix(int index) { lastCharIndex++; // Now the endpoint is the next active point if (!isExplicit()) canonize(); - }; + } /** * Is the tree explicit @@ -292,7 +297,7 @@ private int[] searchEdges(char[] query) { public String toString() { StringBuilder builder = new StringBuilder(); builder.append("String = ").append(this.string).append("\n"); - builder.append("End of word character = ").append(END_SEQ_CHAR).append("\n"); + builder.append("End of word character = ").append(endSeqChar).append("\n"); builder.append(TreePrinter.getString(this)); return builder.toString(); } @@ -337,7 +342,7 @@ public int compareTo(Link link) { return 0; } - }; + } private static class Edge implements Comparable> { @@ -403,17 +408,16 @@ private static Edge find(SuffixTree tree, int nod return tree.edgeMap.get(key); } - private int split(int originNode, int firstCharIndex, int lastCharIndex) { + private int split(int originNode, int firstIndex, int lastIndex) { remove(this); - Edge newEdge = new Edge(tree, this.firstCharIndex, this.firstCharIndex + lastCharIndex - - firstCharIndex, originNode); + Edge newEdge = new Edge(tree, this.firstCharIndex, this.firstCharIndex + lastIndex - firstIndex, originNode); Link link = tree.linksMap.get(newEdge.endNode); if (link == null) { link = new Link(newEdge.endNode); tree.linksMap.put(newEdge.endNode, link); } tree.linksMap.get(newEdge.endNode).suffixNode = originNode; - this.firstCharIndex += lastCharIndex - firstCharIndex + 1; + this.firstCharIndex += lastIndex - firstIndex + 1; this.startNode = newEdge.endNode; insert(this); return newEdge.endNode; @@ -446,21 +450,6 @@ public boolean equals(Object obj) { return false; } - /** - * {@inheritDoc} - */ - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("startNode=").append(startNode).append("\n"); - builder.append("endNode=").append(endNode).append("\n"); - builder.append("firstCharIndex=").append(firstCharIndex).append("\n"); - builder.append("lastCharIndex=").append(lastCharIndex).append("\n"); - String s = tree.string.substring(firstCharIndex, lastCharIndex + 1); - builder.append("string=").append(s).append("\n"); - return builder.toString(); - } - /** * {@inheritDoc} */ @@ -491,6 +480,21 @@ public int compareTo(Edge edge) { return 0; } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("startNode=").append(startNode).append("\n"); + builder.append("endNode=").append(endNode).append("\n"); + builder.append("firstCharIndex=").append(firstCharIndex).append("\n"); + builder.append("lastCharIndex=").append(lastCharIndex).append("\n"); + String s = tree.string.substring(firstCharIndex, lastCharIndex + 1); + builder.append("string=").append(s).append("\n"); + return builder.toString(); + } } protected static class TreePrinter { @@ -510,7 +514,7 @@ private static String getString(SuffixTree tree, Edg if (e != null) { value = e.endNode; String string = tree.string.substring(e.firstCharIndex, e.lastCharIndex + 1); - int index = string.indexOf(tree.END_SEQ_CHAR); + int index = string.indexOf(tree.endSeqChar); if (index >= 0) string = string.substring(0, index + 1); builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + value + ") " + string + "\n"); diff --git a/src/com/jwetherell/algorithms/data_structures/SuffixTrie.java b/src/com/jwetherell/algorithms/data_structures/SuffixTrie.java index 640d2bec..e82af621 100644 --- a/src/com/jwetherell/algorithms/data_structures/SuffixTrie.java +++ b/src/com/jwetherell/algorithms/data_structures/SuffixTrie.java @@ -4,20 +4,23 @@ import java.util.TreeSet; import com.jwetherell.algorithms.data_structures.Trie.Node; +import com.jwetherell.algorithms.data_structures.interfaces.ISuffixTree; /** * A suffix trie is a data structure that presents the suffixes of a given * string in a way that allows for a particularly fast implementation of many - * important string operations. This implementation is based upon a Trie which - * is NOT a compact trie. - * - * http://en.wikipedia.org/wiki/Suffix_trie - * + * important string operations. + *

+ * This implementation is based upon a Trie which is NOT a compact trie. + *

+ * @see Suffix Trie (Wikipedia) + *
* @author Justin Wetherell */ -public class SuffixTrie { +@SuppressWarnings("unchecked") +public class SuffixTrie implements ISuffixTree { - private Trie tree = null; + private Trie tree; /** * Create a suffix trie from sequence @@ -25,7 +28,6 @@ public class SuffixTrie { * @param sequence * to create a suffix trie from. */ - @SuppressWarnings("unchecked") public SuffixTrie(C sequence) { tree = new Trie(); int length = sequence.length(); @@ -42,7 +44,6 @@ public SuffixTrie(C sequence) { * to add to trie. * @return True if added successfully. */ - @SuppressWarnings("unchecked") public boolean add(C sequence) { int length = sequence.length(); for (int i = 0; i < length; i++) { @@ -59,6 +60,7 @@ public boolean add(C sequence) { * to locate in the trie. * @return True if sequence exists in trie. */ + @Override public boolean doesSubStringExist(C sequence) { char[] chars = sequence.toString().toCharArray(); int length = chars.length; @@ -77,6 +79,7 @@ public boolean doesSubStringExist(C sequence) { * * @return set of suffixes in trie. */ + @Override public Set getSuffixes() { return this.getSuffixes(tree.root); } diff --git a/src/com/jwetherell/algorithms/data_structures/TernarySearchTree.java b/src/com/jwetherell/algorithms/data_structures/TernarySearchTree.java new file mode 100644 index 00000000..3c034afc --- /dev/null +++ b/src/com/jwetherell/algorithms/data_structures/TernarySearchTree.java @@ -0,0 +1,442 @@ +package com.jwetherell.algorithms.data_structures; + +import com.jwetherell.algorithms.data_structures.interfaces.ITree; + +/** + * In computer science, a ternary search tree is a type of trie (sometimes called a prefix tree) where nodes are arranged in a manner similar to a binary search tree, but with up to three + * children rather than the binary tree's limit of two. + *
+ * This implementation is based on Jon Bentley and Bob Sedgewick's paper. + *

+ * @see Ternary Search Tree (Wikipedia) + *
+ * @author Justin Wetherell + */ +public class TernarySearchTree implements ITree { + + protected INodeCreator creator; + protected Node root; + + private int size = 0; + + public TernarySearchTree() { + this.creator = new INodeCreator() { + /** + * {@inheritDoc} + */ + @Override + public Node createNewNode(Node parent, Character character, boolean isWord) { + return (new Node(parent, character, isWord)); + } + }; + } + + /** + * Constructor with external Node creator. + */ + public TernarySearchTree(INodeCreator creator) { + this.creator = creator; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean add(C value) { + if (value == null) + return false; + + final int before = size; + if (root == null) + this.root = insert(null, null, value, 0); + else + insert(null, root, value, 0); + final int after = size; + + // Successful if the size has increased by one + return (before==(after-1)); + } + + private Node insert(Node parent, Node node, C value, int idx) { + if (idx >= value.length()) + return null; + + final char c = value.charAt(idx); + final boolean isWord = (idx==(value.length()-1)); + + if (node == null) { + // Create new node + node = this.creator.createNewNode(parent, c, isWord); + + // If new node represents a "word", increase the size + if (isWord) + size++; + } else if (c==node.character && isWord && !node.isWord) { + // Changing an existing node into a "word" node + node.isWord = true; + // Increase the size + size++; + } + + if (c < node.character) { + node.loKid = insert(node, node.loKid, value, idx); + } else if (c > node.character) { + node.hiKid = insert(node, node.hiKid, value, idx); + } else if (idx < (value.length()-1)) { + // Not done with whole string yet + node.kid = insert(node, node.kid, value, ++idx); + } + return node; + } + + /** + * {@inheritDoc} + */ + @Override + public C remove(C value) { + if (value == null || root == null) + return null; + + // Find the node + final Node node = search(root, value, 0); + + // If node was not found or the node is not a "word" + if (node == null || !node.isWord) + return null; + + // Node was found, remove from tree if possible + node.isWord = false; + remove(node); + size--; + return value; + } + + private void remove(Node node) { + // If node is a "word", stop the recursive pruning + if (node.isWord) + return; + + // If node has at least one child, we cannot prune it. + if (node.loKid!=null || node.kid!=null || node.hiKid!=null) + return; + + // Node has no children, prune the node + final Node parent = node.parent; + if (parent != null) { + // Remove node from parent + if (parent.loKid==node) { + parent.loKid = null; + } else if (parent.hiKid==node) { + parent.hiKid = null; + } else if (parent.kid==node) { + parent.kid = null; + } + + // If parent is not a "word" node, go up the tree and prune if possible + if (!node.isWord) + remove(parent); + } else { + // If node doesn't have a parent, it's root. + this.root = null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void clear() { + root = null; + size = 0; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(C value) { + if (value == null) + return false; + + // Find the node + final Node node = search(root, value, 0); + + // If node isn't null and it represents a "word" then the tree contains the value + return (node!=null && node.isWord); + } + + private Node search(Node node, C value, int idx) { + if (node == null || idx>=value.length()) + return null; + + final char c = value.charAt(idx); + + if (c < node.character) + return search(node.loKid, value, idx); + if (c > node.character) + return search(node.hiKid, value, idx); + if (idx < (value.length()-1)) { + // c == node.char and there is still some characters left in the search string + return search(node.kid, value, ++idx); + } + return node; + } + + @Override + public int size() { + return size; + } + + @Override + public boolean validate() { + if (this.root == null) + return true; + return validate(root); + } + + private boolean validate(Node node) { + boolean result = false; + if (node.loKid != null) { + if (node.loKid.character >= node.character) + return false; + result = validate(node.loKid); + if (!result) + return false; + } + + if (node.kid != null) { + result = validate(node.kid); + if (!result) + return false; + } + + if (node.hiKid != null) { + if (node.hiKid.character <= node.character) + return false; + result = validate(node.hiKid); + if (!result) + return false; + } + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public java.util.Collection toCollection() { + return (new JavaCompatibleTree(this)); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return TreePrinter.getString(this); + } + + protected static interface INodeCreator { + + /** + * Create a new node for sequence. + * + * @param parent + * node of the new node. + * @param character + * which represents this node. + * @param isWord + * signifies if the node represents a word. + * @return Node which was created. + */ + public Node createNewNode(Node parent, Character character, boolean isWord); + } + + protected static class TreePrinter { + + public static void print(TernarySearchTree tree) { + System.out.println(getString(tree)); + } + + public static String getString(TernarySearchTree tree) { + if (tree.root == null) + return "Tree has no nodes."; + return getString(tree.root, "", null, true); + } + + protected static String getString(Node node, String prefix, String previousString, boolean isTail) { + final StringBuilder builder = new StringBuilder(); + String string = ""; + if (previousString != null) + string = previousString; + builder.append(prefix + (isTail ? "└── " : "├── ") + ((node.isWord == true) ? + ("(" + node.character + ") " + string+String.valueOf(node.character)) + : + node.character) + "\n" + ); + if (node.loKid != null) + builder.append(getString(node.loKid, prefix + (isTail ? " " : "│ "), string, false)); + if (node.kid != null) + builder.append(getString(node.kid, prefix + (isTail ? " " : "│ "), string+String.valueOf(node.character), false)); + if (node.hiKid != null) + builder.append(getString(node.hiKid, prefix + (isTail ? " " : "│ "), string, true)); + return builder.toString(); + } + } + + protected static class Node { + + private final Node parent; + private final char character; + + private boolean isWord; + + protected Node loKid; + protected Node kid; + protected Node hiKid; + + protected Node(Node parent, char character, boolean isWord) { + this.parent = parent; + this.character = character; + this.isWord = isWord; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("char=").append(this.character).append('\n'); + if (this.loKid != null) + builder.append('\t').append("lo=").append(this.loKid.character).append('\n'); + if (this.kid != null) + builder.append('\t').append("eq=").append(this.kid.character).append('\n'); + if (this.hiKid != null) + builder.append('\t').append("hi=").append(this.hiKid.character).append('\n'); + return builder.toString(); + } + } + + @SuppressWarnings("unchecked") + public static class JavaCompatibleTree extends java.util.AbstractCollection { + + private TernarySearchTree tree = null; + + public JavaCompatibleTree(TernarySearchTree tree) { + this.tree = tree; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean add(C value) { + return tree.add(value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean remove(Object value) { + return (tree.remove((C)value)!=null); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Object value) { + return tree.contains((C)value); + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return tree.size; + } + + /** + * {@inheritDoc} + * + * WARNING: This iterator makes a copy of the tree's contents during it's construction! + */ + @Override + public java.util.Iterator iterator() { + return (new TreeIterator(tree)); + } + + private static class TreeIterator implements java.util.Iterator { + + private TernarySearchTree tree = null; + private TernarySearchTree.Node lastNode = null; + private java.util.Iterator> iterator = null; + + protected TreeIterator(TernarySearchTree tree) { + this.tree = tree; + java.util.Map map = new java.util.LinkedHashMap(); + if (this.tree.root!=null) { + getNodesWhichRepresentsWords(this.tree.root,"",map); + } + iterator = map.entrySet().iterator(); + } + + private void getNodesWhichRepresentsWords(TernarySearchTree.Node node, String string, java.util.Map nodesMap) { + final StringBuilder builder = new StringBuilder(string); + if (node.isWord) + nodesMap.put(node,builder.toString()); + if (node.loKid != null) { + Node child = node.loKid; + getNodesWhichRepresentsWords(child, builder.toString(), nodesMap); + } + if (node.kid != null) { + Node child = node.kid; + getNodesWhichRepresentsWords(child, builder.append(node.character).toString(), nodesMap); + } + if (node.hiKid != null) { + Node child = node.hiKid; + getNodesWhichRepresentsWords(child, builder.toString(), nodesMap); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasNext() { + if (iterator!=null && iterator.hasNext()) + return true; + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public C next() { + if (iterator==null) + return null; + + java.util.Map.Entry entry = iterator.next(); + lastNode = entry.getKey(); + return (C)entry.getValue(); + } + + /** + * {@inheritDoc} + */ + @Override + public void remove() { + if (iterator==null || tree==null) + return; + + iterator.remove(); + this.tree.remove(lastNode); + } + } + } +} diff --git a/src/com/jwetherell/algorithms/data_structures/Treap.java b/src/com/jwetherell/algorithms/data_structures/Treap.java index ec773d1d..01ea0c45 100644 --- a/src/com/jwetherell/algorithms/data_structures/Treap.java +++ b/src/com/jwetherell/algorithms/data_structures/Treap.java @@ -12,12 +12,12 @@ * particular, with high probability its height is proportional to the logarithm * of the number of values, so that each search, insertion, or deletion * operation takes logarithmic time to perform. - * - * http://en.wikipedia.org/wiki/Treap - * + *

+ * @see Treap (Wikipedia) + *
* @author Justin Wetherell */ -public class Treap> extends BinarySearchTree implements BinarySearchTree.INodeCreator { +public class Treap> extends BinarySearchTree { private static int randomSeed = Integer.MAX_VALUE; // This should be at least twice the number of Nodes @@ -25,7 +25,15 @@ public class Treap> extends BinarySearchTree implemen * Default constructor. */ public Treap() { - this.creator = this; + this.creator = new BinarySearchTree.INodeCreator() { + /** + * {@inheritDoc} + */ + @Override + public BinarySearchTree.Node createNewNode(BinarySearchTree.Node parent, T id) { + return (new TreapNode(parent, id)); + } + }; } /** @@ -79,16 +87,16 @@ private void heapify(TreapNode current) { while (parent != null && current.priority > parent.priority) { Node grandParent = parent.parent; if (grandParent != null) { - if (grandParent.greater != null && grandParent.greater.equals(parent)) { + if (grandParent.greater != null && grandParent.greater == parent) { // My parent is my grandparents greater branch grandParent.greater = current; current.parent = grandParent; - } else if (grandParent.lesser != null && grandParent.lesser.equals(parent)) { + } else if (grandParent.lesser != null && grandParent.lesser == parent) { // My parent is my grandparents lesser branch grandParent.lesser = current; current.parent = grandParent; } else { - System.err.println("YIKES! Grandparent should have at least one non-NULL child which should be my parent."); + throw new RuntimeException("YIKES! Grandparent should have at least one non-NULL child which should be my parent."); } current.parent = grandParent; } else { @@ -96,7 +104,7 @@ private void heapify(TreapNode current) { root.parent = null; } - if (parent.lesser != null && parent.lesser.equals(current)) { + if (parent.lesser != null && parent.lesser == current) { // LEFT parent.lesser = null; @@ -111,7 +119,7 @@ private void heapify(TreapNode current) { parent.lesser = lost; lost.parent = parent; } - } else if (parent.greater != null && parent.greater.equals(current)) { + } else if (parent.greater != null && parent.greater == current) { // RIGHT parent.greater = null; @@ -128,7 +136,7 @@ private void heapify(TreapNode current) { } } else { // We really shouldn't get here - System.err.println("YIKES! Parent should have at least one non-NULL child which should be me."); + throw new RuntimeException("YIKES! Parent should have at least one non-NULL child which should be me."); } parent = (TreapNode) current.parent; @@ -143,24 +151,16 @@ public String toString() { return TreapPrinter.getString(this); } - /** - * {@inheritDoc} - */ - @Override - public Node createNewNode(Node parent, T id) { - return (new TreapNode(null, id)); - } - private static class TreapNode> extends Node { private int priority = Integer.MIN_VALUE; - private TreapNode(TreapNode parent, int priority, T value) { + private TreapNode(Node parent, int priority, T value) { super(parent, value); this.priority = priority; } - private TreapNode(TreapNode parent, T value) { + private TreapNode(Node parent, T value) { this(parent, RANDOM.nextInt(randomSeed), value); } @@ -182,7 +182,8 @@ public String toString() { protected static class TreapPrinter { public static > String getString(Treap tree) { - if (tree.root == null) return "Tree has no nodes."; + if (tree.root == null) + return "Tree has no nodes."; return getString((TreapNode) tree.root, "", true); } diff --git a/src/com/jwetherell/algorithms/data_structures/TreeMap.java b/src/com/jwetherell/algorithms/data_structures/TreeMap.java index c05d1581..728d7750 100644 --- a/src/com/jwetherell/algorithms/data_structures/TreeMap.java +++ b/src/com/jwetherell/algorithms/data_structures/TreeMap.java @@ -3,26 +3,46 @@ import java.util.ArrayList; import java.util.List; +import com.jwetherell.algorithms.data_structures.BinarySearchTree.INodeCreator; import com.jwetherell.algorithms.data_structures.BinarySearchTree.Node; +import com.jwetherell.algorithms.data_structures.interfaces.IMap; /** * An tree used to store key->values pairs, this is an implementation of an * associative array. - * + *

* This implementation is a composition of a AVLTree as the backing structure. - * - * http://en.wikipedia.org/wiki/AVL_tree - * http://en.wikipedia.org/wiki/Associative_array - * + *

+ * @see AVL Tree (Wikipedia) + * @see Associative Array (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") -public class TreeMap, V> implements BinarySearchTree.INodeCreator, IMap { +public class TreeMap, V> implements IMap { + + private final BinarySearchTree.INodeCreator creator = new BinarySearchTree.INodeCreator() { + /** + * {@inheritDoc} + */ + @Override + public BinarySearchTree.Node createNewNode(BinarySearchTree.Node parent, K id) { + return (new TreeMapNode(parent, id, null)); + } + }; private AVLTree tree = null; public TreeMap() { - tree = new AVLTree(this); + + tree = new AVLTree(creator); + } + + /** + * Constructor with external Node creator. + */ + public TreeMap(INodeCreator creator) { + tree = new AVLTree(creator); } /** @@ -33,11 +53,10 @@ public V put(K key, V value) { V prev = null; BinarySearchTree.Node node = tree.addValue(key); - if (node instanceof TreeMapNode) { - TreeMapNode treeMapNode = (TreeMapNode) node; - if (treeMapNode.value!=null) prev = treeMapNode.value; - treeMapNode.value = value; - } + TreeMapNode treeMapNode = (TreeMapNode) node; + if (treeMapNode.value!=null) + prev = treeMapNode.value; + treeMapNode.value = value; return prev; } @@ -48,11 +67,8 @@ public V put(K key, V value) { @Override public V get(K key) { BinarySearchTree.Node node = tree.getNode(key); - if (node instanceof TreeMapNode) { - TreeMapNode mapNode = (TreeMapNode) node; - return mapNode.value; - } - return null; + TreeMapNode mapNode = (TreeMapNode) node; + return mapNode.value; } /** @@ -69,10 +85,12 @@ public boolean contains(K key) { @Override public V remove(K key) { Node node = tree.removeValue(key); + TreeMapNode treeMapNode = (TreeMapNode) node; V value = null; - if (node instanceof TreeMapNode) { - TreeMapNode treeMapNode = (TreeMapNode) node; + if (treeMapNode!=null) { value = treeMapNode.value; + treeMapNode.id = null; + treeMapNode.value = null; } return value; } @@ -100,21 +118,27 @@ public int size() { public boolean validate() { java.util.Set keys = new java.util.HashSet(); Node node = tree.root; - if (node!=null && !validate(node,keys)) return false; + if (node!=null && !validate(node,keys)) + return false; return (keys.size()==size()); } private boolean validate(Node node, java.util.Set keys) { - if (!(node instanceof TreeMapNode)) return false; + if (!(node instanceof TreeMapNode)) + return false; TreeMapNode tmn = (TreeMapNode)node; K k = tmn.id; V v = tmn.value; - if (k==null || v==null) return false; - if (keys.contains(k)) return false; + if (k==null || v==null) + return false; + if (keys.contains(k)) + return false; keys.add(k); - if (tmn.lesser!=null && !validate(tmn.lesser,keys)) return false; - if (tmn.greater!=null && !validate(tmn.greater,keys)) return false; + if (tmn.lesser!=null && !validate(tmn.lesser,keys)) + return false; + if (tmn.greater!=null && !validate(tmn.greater,keys)) + return false; return true; } @@ -134,14 +158,6 @@ public String toString() { return TreeMapPrinter.getString(this); } - /** - * {@inheritDoc} - */ - @Override - public AVLTree.Node createNewNode(AVLTree.Node parent, K id) { - return (new TreeMapNode(parent, id, null)); - } - protected static class TreeMapNode, V> extends AVLTree.AVLNode { protected V value = null; diff --git a/src/com/jwetherell/algorithms/data_structures/Trie.java b/src/com/jwetherell/algorithms/data_structures/Trie.java index 7f313ecc..051035ea 100644 --- a/src/com/jwetherell/algorithms/data_structures/Trie.java +++ b/src/com/jwetherell/algorithms/data_structures/Trie.java @@ -2,25 +2,37 @@ import java.util.Arrays; +import com.jwetherell.algorithms.data_structures.interfaces.ITree; + /** * A trie, or prefix tree, is an ordered tree data structure that is used to * store an associative array where the keys are usually strings. - * - * == This is NOT a compact Trie. == - * - * http://en.wikipedia.org/wiki/Trie - * + *

+ * NOTE: This is NOT a compact Trie + *

+ * @see Trie (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") public class Trie implements ITree { - private int size = 0; + protected INodeCreator creator; + protected Node root; - protected INodeCreator creator = null; - protected Node root = null; + private int size = 0; - public Trie() { } + public Trie() { + this.creator = new INodeCreator() { + /** + * {@inheritDoc} + */ + @Override + public Node createNewNode(Node parent, Character character, boolean isWord) { + return (new Node(parent, character, isWord)); + } + }; + } /** * Constructor with external Node creator. @@ -29,21 +41,6 @@ public Trie(INodeCreator creator) { this.creator = creator; } - /** - * Create a new node for sequence. - * - * @param parent - * node of the new node. - * @param character - * which represents this node. - * @param isWord - * signifies if the node represents a word. - * @return Node which was created. - */ - protected Node createNewNode(Node parent, Character character, boolean isWord) { - return (new Node(parent, character, isWord)); - } - /** * {@inheritDoc} */ @@ -60,12 +57,8 @@ public boolean add(C seq) { * @return Node which was added to trie or null if it already exists. */ protected Node addSequence(C seq) { - if (root == null) { - if (this.creator == null) - root = createNewNode(null, Node.SENTINAL, false); - else - root = this.creator.createNewNode(null, Node.SENTINAL, false); - } + if (root == null) + root = this.creator.createNewNode(null, Node.SENTINAL, false); int length = (seq.length() - 1); Node prev = root; @@ -81,10 +74,7 @@ protected Node addSequence(C seq) { n = prev.getChild(index); } else { // Create a new child for the character - if (this.creator == null) - n = createNewNode(prev, c, false); - else - n = this.creator.createNewNode(prev, c, false); + n = this.creator.createNewNode(prev, c, false); prev.addChild(n); } prev = n; @@ -110,10 +100,7 @@ protected Node addSequence(C seq) { return null; } // Create a new node for the input string - if (this.creator == null) - n = createNewNode(prev, c, true); - else - n = this.creator.createNewNode(prev, c, true); + n = this.creator.createNewNode(prev, c, true); prev.addChild(n); size++; return n; @@ -308,10 +295,10 @@ protected boolean removeChild(int index) { return true; } - protected int childIndex(Character character) { + protected int childIndex(Character parentChar) { for (int i = 0; i < childrenSize; i++) { - Node c = children[i]; - if (character.equals(c.character)) return i; + Node childChar = children[i]; + if (parentChar.equals(childChar.character)) return i; } return Integer.MIN_VALUE; } @@ -353,7 +340,7 @@ protected static interface INodeCreator { * signifies if the node represents a word. * @return Node which was created. */ - public Node createNewNode(Node parent, Character character, boolean type); + public Node createNewNode(Node parent, Character character, boolean isWord); } protected static class TriePrinter { @@ -363,10 +350,12 @@ public static void print(Trie trie) { } public static String getString(Trie tree) { + if (tree.root == null) + return "Tree has no nodes."; return getString(tree.root, "", null, true); } - protected static String getString(Node node, String prefix, String previousString, boolean isTail) { + protected static String getString(Node node, String prefix, String previousString, boolean isTail) { StringBuilder builder = new StringBuilder(); String string = null; if (node.character != Node.SENTINAL) { diff --git a/src/com/jwetherell/algorithms/data_structures/TrieMap.java b/src/com/jwetherell/algorithms/data_structures/TrieMap.java index c05fa2bf..06037c7d 100644 --- a/src/com/jwetherell/algorithms/data_structures/TrieMap.java +++ b/src/com/jwetherell/algorithms/data_structures/TrieMap.java @@ -1,16 +1,17 @@ package com.jwetherell.algorithms.data_structures; import com.jwetherell.algorithms.data_structures.Trie.Node; +import com.jwetherell.algorithms.data_structures.interfaces.IMap; /** * A trie used to store key->values pairs, this is an implementation of an * associative array. - * + *

* This implementation is a composition using a Trie as the backing structure. - * - * http://en.wikipedia.org/wiki/Trie - * http://en.wikipedia.org/wiki/Associative_array - * + *

+ * @see Trie (Wikipedia) + * @see Associative Array (Wikipedia) + *
* @author Justin Wetherell */ @SuppressWarnings("unchecked") @@ -71,6 +72,7 @@ public V remove(K key) { if (node instanceof TrieMapNode) { TrieMapNode tmn = (TrieMapNode)node; value = tmn.value; + tmn.value = null; } trie.remove(node); } @@ -186,7 +188,7 @@ public static String getString(TrieMap map) { return getString(map.trie.root, "", null, true); } - protected static String getString(Trie.Node node, String prefix, String previousString, boolean isTail) { + protected static String getString(Trie.Node node, String prefix, String previousString, boolean isTail) { StringBuilder builder = new StringBuilder(); String string = null; if (node.character != Node.SENTINAL) { diff --git a/src/com/jwetherell/algorithms/data_structures/IHeap.java b/src/com/jwetherell/algorithms/data_structures/interfaces/IHeap.java similarity index 92% rename from src/com/jwetherell/algorithms/data_structures/IHeap.java rename to src/com/jwetherell/algorithms/data_structures/interfaces/IHeap.java index 0dfcb876..14e9ec8f 100644 --- a/src/com/jwetherell/algorithms/data_structures/IHeap.java +++ b/src/com/jwetherell/algorithms/data_structures/interfaces/IHeap.java @@ -1,4 +1,4 @@ -package com.jwetherell.algorithms.data_structures; +package com.jwetherell.algorithms.data_structures.interfaces; /** * In computer science, a heap is a specialized tree-based data structure that @@ -7,9 +7,9 @@ * the keys of parent nodes are always greater than or equal to those of the children * and the highest key is in the root node (this kind of heap is called max heap) or * the keys of parent nodes are less than or equal to those of the children (min heap). - * - * http://en.wikipedia.org/wiki/Heap - * + *

+ * @see Heap (Wikipedia) + *
* @author Justin Wetherell */ public interface IHeap { diff --git a/src/com/jwetherell/algorithms/data_structures/IList.java b/src/com/jwetherell/algorithms/data_structures/interfaces/IList.java similarity index 89% rename from src/com/jwetherell/algorithms/data_structures/IList.java rename to src/com/jwetherell/algorithms/data_structures/interfaces/IList.java index 26d5cfb1..1cc75929 100644 --- a/src/com/jwetherell/algorithms/data_structures/IList.java +++ b/src/com/jwetherell/algorithms/data_structures/interfaces/IList.java @@ -1,11 +1,11 @@ -package com.jwetherell.algorithms.data_structures; +package com.jwetherell.algorithms.data_structures.interfaces; /** * A list or sequence is an abstract data type that implements an ordered * collection of values, where the same value may occur more than once. - * - * http://en.wikipedia.org/wiki/List_(computing) - * + *

+ * @see List (Wikipedia) + *
* @author Justin Wetherell */ public interface IList { diff --git a/src/com/jwetherell/algorithms/data_structures/IMap.java b/src/com/jwetherell/algorithms/data_structures/interfaces/IMap.java similarity index 89% rename from src/com/jwetherell/algorithms/data_structures/IMap.java rename to src/com/jwetherell/algorithms/data_structures/interfaces/IMap.java index 0ce9a455..c47df1db 100644 --- a/src/com/jwetherell/algorithms/data_structures/IMap.java +++ b/src/com/jwetherell/algorithms/data_structures/interfaces/IMap.java @@ -1,12 +1,12 @@ -package com.jwetherell.algorithms.data_structures; +package com.jwetherell.algorithms.data_structures.interfaces; /** * In computer science, an associative array, map, or dictionary is an abstract * data type composed of a collection of (key, value) pairs, such that each possible * key appears at most once in the collection. - * - * http://en.wikipedia.org/wiki/Associative_array - * + *

+ * @see Associative Array (Wikipedia) + *
* @author Justin Wetherell */ public interface IMap { diff --git a/src/com/jwetherell/algorithms/data_structures/IQueue.java b/src/com/jwetherell/algorithms/data_structures/interfaces/IQueue.java similarity index 92% rename from src/com/jwetherell/algorithms/data_structures/IQueue.java rename to src/com/jwetherell/algorithms/data_structures/interfaces/IQueue.java index d1eddab0..a5c9af8c 100644 --- a/src/com/jwetherell/algorithms/data_structures/IQueue.java +++ b/src/com/jwetherell/algorithms/data_structures/interfaces/IQueue.java @@ -1,4 +1,4 @@ -package com.jwetherell.algorithms.data_structures; +package com.jwetherell.algorithms.data_structures.interfaces; /** * A queue is a particular kind of abstract data type or collection in @@ -8,9 +8,9 @@ * This makes the queue a First-In-First-Out (FIFO) data structure. In a FIFO * data structure, the first element added to the queue will be the first one to * be removed. - * - * http://en.wikipedia.org/wiki/Queue_(abstract_data_type) - * + *

+ * @see Queue (Wikipedia) + *
* @author Justin Wetherell */ public interface IQueue { diff --git a/src/com/jwetherell/algorithms/data_structures/ISet.java b/src/com/jwetherell/algorithms/data_structures/interfaces/ISet.java similarity index 90% rename from src/com/jwetherell/algorithms/data_structures/ISet.java rename to src/com/jwetherell/algorithms/data_structures/interfaces/ISet.java index 28fb17ae..3e565190 100644 --- a/src/com/jwetherell/algorithms/data_structures/ISet.java +++ b/src/com/jwetherell/algorithms/data_structures/interfaces/ISet.java @@ -1,13 +1,13 @@ -package com.jwetherell.algorithms.data_structures; +package com.jwetherell.algorithms.data_structures.interfaces; /** * In computer science, a set is an abstract data structure that can store certain values, without * any particular order, and no repeated values. It is a computer implementation of the mathematical * concept of a finite set. Unlike most other collection types, rather than retrieving a specific * element from a set, one typically tests a value for membership in a set. - * - * http://en.wikipedia.org/wiki/Set_(abstract_data_type) - * + *

+ * @see Set (Wikipedia) + *
* @author Justin Wetherell */ public interface ISet { diff --git a/src/com/jwetherell/algorithms/data_structures/IStack.java b/src/com/jwetherell/algorithms/data_structures/interfaces/IStack.java similarity index 92% rename from src/com/jwetherell/algorithms/data_structures/IStack.java rename to src/com/jwetherell/algorithms/data_structures/interfaces/IStack.java index d8dfd802..cf2d41b5 100644 --- a/src/com/jwetherell/algorithms/data_structures/IStack.java +++ b/src/com/jwetherell/algorithms/data_structures/interfaces/IStack.java @@ -1,4 +1,4 @@ -package com.jwetherell.algorithms.data_structures; +package com.jwetherell.algorithms.data_structures.interfaces; /** * A stack is a last in, first out (LIFO) abstract data type and linear @@ -8,9 +8,9 @@ * if it is empty. If the stack is full and does not contain enough space to * accept the given item, the stack is then considered to be in an overflow * state. The pop operation removes an item from the top of the stack. - * - * http://en.wikipedia.org/wiki/Stack_(abstract_data_type) - * + *

+ * @see Stack (Wikipedia) + *
* @author Justin Wetherell */ public interface IStack { diff --git a/src/com/jwetherell/algorithms/data_structures/interfaces/ISuffixTree.java b/src/com/jwetherell/algorithms/data_structures/interfaces/ISuffixTree.java new file mode 100644 index 00000000..64fd913c --- /dev/null +++ b/src/com/jwetherell/algorithms/data_structures/interfaces/ISuffixTree.java @@ -0,0 +1,30 @@ +package com.jwetherell.algorithms.data_structures.interfaces; + +import java.util.Set; + +/** + * In computer science, a suffix tree (also called PAT tree or, in an earlier form, position tree) is a compressed trie + * containing all the suffixes of the given text as their keys and positions in the text as their values. Suffix trees + * allow particularly fast implementations of many important string operations. + *

+ * @see Suffix Tree (Wikipedia) + *
+ * @author Justin Wetherell + */ +public interface ISuffixTree { + + /** + * Does the sub-sequence exist in the suffix tree. + * + * @param sub-sequence to locate in the tree. + * @return True if the sub-sequence exist in the tree. + */ + public boolean doesSubStringExist(C sub); + + /** + * Get all the suffixes in the tree. + * + * @return set of suffixes in the tree. + */ + public Set getSuffixes(); +} diff --git a/src/com/jwetherell/algorithms/data_structures/ITree.java b/src/com/jwetherell/algorithms/data_structures/interfaces/ITree.java similarity index 90% rename from src/com/jwetherell/algorithms/data_structures/ITree.java rename to src/com/jwetherell/algorithms/data_structures/interfaces/ITree.java index 297e41a4..1e48299b 100644 --- a/src/com/jwetherell/algorithms/data_structures/ITree.java +++ b/src/com/jwetherell/algorithms/data_structures/interfaces/ITree.java @@ -1,13 +1,13 @@ -package com.jwetherell.algorithms.data_structures; +package com.jwetherell.algorithms.data_structures.interfaces; /** * A tree can be defined recursively (locally) as a collection of nodes (starting at a root node), * where each node is a data structure consisting of a value, together with a list of nodes (the "children"), * with the constraints that no node is duplicated. A tree can be defined abstractly as a whole (globally) * as an ordered tree, with a value assigned to each node. - * - * http://en.wikipedia.org/wiki/Tree_(data_structure) - * + *

+ * @see Tree (Wikipedia) + *
* @author Justin Wetherell */ public interface ITree { diff --git a/src/com/jwetherell/algorithms/graph/AStar.java b/src/com/jwetherell/algorithms/graph/AStar.java new file mode 100644 index 00000000..c8e3c751 --- /dev/null +++ b/src/com/jwetherell/algorithms/graph/AStar.java @@ -0,0 +1,137 @@ +package com.jwetherell.algorithms.graph; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.data_structures.Graph.Edge; +import com.jwetherell.algorithms.data_structures.Graph.Vertex; + +/** + * In computer science, A* is a computer algorithm that is widely used in path finding and graph traversal, the process + * of plotting an efficiently traversable path between multiple points, called nodes. + *

+ * @see A* Algorithm (Wikipedia) + *
+ * @author Justin Wetherell + */ +public class AStar> { + + public AStar() { } + + /** + * Find the path using the A* algorithm from start vertex to end vertex or NULL if no path exists. + * + * @param graph + * Graph to search. + * @param start + * Start vertex. + * @param goal + * Goal vertex. + * + * @return + * List of Edges to get from start to end or NULL if no path exists. + */ + public List> aStar(Graph graph, Graph.Vertex start, Graph.Vertex goal) { + final int size = graph.getVertices().size(); // used to size data structures appropriately + final Set> closedSet = new HashSet>(size); // The set of nodes already evaluated. + final List> openSet = new ArrayList>(size); // The set of tentative nodes to be evaluated, initially containing the start node + openSet.add(start); + final Map,Graph.Vertex> cameFrom = new HashMap,Graph.Vertex>(size); // The map of navigated nodes. + + final Map,Integer> gScore = new HashMap,Integer>(); // Cost from start along best known path. + gScore.put(start, 0); + + // Estimated total cost from start to goal through y. + final Map,Integer> fScore = new HashMap,Integer>(); + for (Graph.Vertex v : graph.getVertices()) + fScore.put(v, Integer.MAX_VALUE); + fScore.put(start, heuristicCostEstimate(start,goal)); + + final Comparator> comparator = new Comparator>() { + /** + * {@inheritDoc} + */ + @Override + public int compare(Vertex o1, Vertex o2) { + if (fScore.get(o1) < fScore.get(o2)) + return -1; + if (fScore.get(o2) < fScore.get(o1)) + return 1; + return 0; + } + }; + + while (!openSet.isEmpty()) { + final Graph.Vertex current = openSet.get(0); + if (current.equals(goal)) + return reconstructPath(cameFrom, goal); + + openSet.remove(0); + closedSet.add(current); + for (Graph.Edge edge : current.getEdges()) { + final Graph.Vertex neighbor = edge.getToVertex(); + if (closedSet.contains(neighbor)) + continue; // Ignore the neighbor which is already evaluated. + + final int tenativeGScore = gScore.get(current) + distanceBetween(current,neighbor); // length of this path. + if (!openSet.contains(neighbor)) + openSet.add(neighbor); // Discover a new node + else if (tenativeGScore >= gScore.get(neighbor)) + continue; + + // This path is the best until now. Record it! + cameFrom.put(neighbor, current); + gScore.put(neighbor, tenativeGScore); + final int estimatedFScore = gScore.get(neighbor) + heuristicCostEstimate(neighbor, goal); + fScore.put(neighbor, estimatedFScore); + + // fScore has changed, re-sort the list + Collections.sort(openSet,comparator); + } + } + + return null; + } + + /** + * Default distance is the edge cost. If there is no edge between the start and next then + * it returns Integer.MAX_VALUE; + */ + protected int distanceBetween(Graph.Vertex start, Graph.Vertex next) { + for (Edge e : start.getEdges()) { + if (e.getToVertex().equals(next)) + return e.getCost(); + } + return Integer.MAX_VALUE; + } + + /** + * Default heuristic: cost to each vertex is 1. + */ + @SuppressWarnings("unused") + protected int heuristicCostEstimate(Graph.Vertex start, Graph.Vertex goal) { + return 1; + } + + private List> reconstructPath(Map,Graph.Vertex> cameFrom, Graph.Vertex current) { + final List> totalPath = new ArrayList>(); + + while (current != null) { + final Graph.Vertex previous = current; + current = cameFrom.get(current); + if (current != null) { + final Graph.Edge edge = current.getEdge(previous); + totalPath.add(edge); + } + } + Collections.reverse(totalPath); + return totalPath; + } +} diff --git a/src/com/jwetherell/algorithms/graph/BellmanFord.java b/src/com/jwetherell/algorithms/graph/BellmanFord.java index 21c837e0..a7e0b56c 100644 --- a/src/com/jwetherell/algorithms/graph/BellmanFord.java +++ b/src/com/jwetherell/algorithms/graph/BellmanFord.java @@ -1,10 +1,9 @@ package com.jwetherell.algorithms.graph; +import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.TreeMap; import com.jwetherell.algorithms.data_structures.Graph; @@ -12,99 +11,113 @@ * Bellman-Ford's shortest path. Works on both negative and positive weighted * edges. Also detects negative weight cycles. Returns a tuple of total cost of * shortest path and the path. - * + *

* Worst case: O(|V| |E|) - * + *

+ * @see Bellman Ford (Wikipedia) + *
* @author Justin Wetherell */ public class BellmanFord { - private static Map, Graph.CostVertexPair> costs = null; - private static Map, Set>> paths = null; - private static boolean containsNegativeWeightCycle = false; - private BellmanFord() { } - public static Map, Graph.CostPathPair> getShortestPaths(Graph g, Graph.Vertex start) { - getShortestPath(g, start, null); - Map, Graph.CostPathPair> map = new HashMap, Graph.CostPathPair>(); + /** + * Get shortest path for all vertices + */ + public static Map, Graph.CostPathPair> getShortestPaths(Graph graph, Graph.Vertex start) { + final Map, List>> paths = new HashMap, List>>(); + final Map, Graph.CostVertexPair> costs = new HashMap, Graph.CostVertexPair>(); + + getShortestPath(graph, start, paths, costs); + + final Map, Graph.CostPathPair> map = new HashMap, Graph.CostPathPair>(); for (Graph.CostVertexPair pair : costs.values()) { - int cost = pair.getCost(); - Graph.Vertex vertex = pair.getVertex(); - Set> path = paths.get(vertex); + final int cost = pair.getCost(); + final Graph.Vertex vertex = pair.getVertex(); + final List> path = paths.get(vertex); map.put(vertex, new Graph.CostPathPair(cost, path)); } return map; } - public static Graph.CostPathPair getShortestPath(Graph g, Graph.Vertex start, Graph.Vertex end) { - if (g == null) + /** + * Get shortest path to from 'start' to 'end' vertices + */ + public static Graph.CostPathPair getShortestPath(Graph graph, Graph.Vertex start, Graph.Vertex end) { + if (graph == null) throw (new NullPointerException("Graph must be non-NULL.")); - // Reset variables - costs = null; - paths = null; - containsNegativeWeightCycle = false; + final Map, List>> paths = new HashMap, List>>(); + final Map, Graph.CostVertexPair> costs = new HashMap, Graph.CostVertexPair>(); + return getShortestPath(graph, start, end, paths, costs); + } - paths = new TreeMap, Set>>(); - for (Graph.Vertex v : g.getVerticies()) { - paths.put(v, new LinkedHashSet>()); - } + private static Graph.CostPathPair getShortestPath(Graph graph, + Graph.Vertex start, Graph.Vertex end, + Map, List>> paths, + Map, Graph.CostVertexPair> costs) { + if (end == null) + throw (new NullPointerException("end must be non-NULL.")); + + getShortestPath(graph, start, paths, costs); - costs = new TreeMap, Graph.CostVertexPair>(); - for (Graph.Vertex v : g.getVerticies()) { + final Graph.CostVertexPair pair = costs.get(end); + final List> list = paths.get(end); + return (new Graph.CostPathPair(pair.getCost(), list)); + } + + private static void getShortestPath(Graph graph, + Graph.Vertex start, + Map, List>> paths, + Map, Graph.CostVertexPair> costs) { + if (graph == null) + throw (new NullPointerException("Graph must be non-NULL.")); + if (start == null) + throw (new NullPointerException("start must be non-NULL.")); + + for (Graph.Vertex v : graph.getVertices()) + paths.put(v, new ArrayList>()); + + // All vertices are INFINITY unless it's the start vertices + for (Graph.Vertex v : graph.getVertices()) if (v.equals(start)) costs.put(v, new Graph.CostVertexPair(0, v)); else costs.put(v, new Graph.CostVertexPair(Integer.MAX_VALUE, v)); - } boolean negativeCycleCheck = false; - for (int i = 0; i < (g.getVerticies().size()); i++) { - - // If it's the last vertices perform a negative weight cycle check. - // The graph should be - // finished by the size()-1 time through this loop. - if (i == (g.getVerticies().size() - 1)) + for (int i = 0; i < graph.getVertices().size(); i++) { + // If it's the last vertices, perform a negative weight cycle check. + // The graph should be finished by the size()-1 time through this loop. + if (i == (graph.getVertices().size() - 1)) negativeCycleCheck = true; // Compute costs to all vertices - for (Graph.Edge e : g.getEdges()) { - Graph.CostVertexPair pair = costs.get(e.getToVertex()); - Graph.CostVertexPair lowestCostToThisVertex = costs.get(e.getFromVertex()); + for (Graph.Edge e : graph.getEdges()) { + final Graph.CostVertexPair pair = costs.get(e.getToVertex()); + final Graph.CostVertexPair lowestCostToThisVertex = costs.get(e.getFromVertex()); - // If the cost of the from vertex is MAX_VALUE then treat as - // INIFINITY. + // If the cost of the from vertex is MAX_VALUE then treat as INIFINITY. if (lowestCostToThisVertex.getCost() == Integer.MAX_VALUE) continue; - int cost = lowestCostToThisVertex.getCost() + e.getCost(); + final int cost = lowestCostToThisVertex.getCost() + e.getCost(); if (cost < pair.getCost()) { + // Found a shorter path to a reachable vertex + pair.setCost(cost); + if (negativeCycleCheck) { // Uhh ohh... negative weight cycle - System.out.println("Graph contains a negative weight cycle."); - containsNegativeWeightCycle = true; - return null; + throw new IllegalArgumentException("Graph contains a negative weight cycle."); } - // Found a shorter path to a reachable vertex - pair.setCost(cost); - Set> set = paths.get(e.getToVertex()); - set.clear(); - set.addAll(paths.get(e.getFromVertex())); - set.add(e); + + final List> list = paths.get(e.getToVertex()); + list.clear(); + list.addAll(paths.get(e.getFromVertex())); + list.add(e); } } } - - if (end != null) { - Graph.CostVertexPair pair = costs.get(end); - Set> set = paths.get(end); - return (new Graph.CostPathPair(pair.getCost(), set)); - } - return null; - } - - public static boolean containsNegativeWeightCycle() { - return containsNegativeWeightCycle; } } diff --git a/src/com/jwetherell/algorithms/graph/BreadthFirstTraversal.java b/src/com/jwetherell/algorithms/graph/BreadthFirstTraversal.java new file mode 100644 index 00000000..d6e1b8e4 --- /dev/null +++ b/src/com/jwetherell/algorithms/graph/BreadthFirstTraversal.java @@ -0,0 +1,124 @@ +package com.jwetherell.algorithms.graph; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.data_structures.Graph.Edge; +import com.jwetherell.algorithms.data_structures.Graph.Vertex; + +/** + * Breadth-first search (BFS) is an algorithm for traversing or searching tree or graph data structures. It starts at the tree root (or some arbitrary node of a graph, sometimes referred to as a + * 'search key') and explores the neighbor nodes first, before moving to the next level neighbors. + *

+ * @see Breadth-First Search (Wikipedia) + *
+ * @author Justin Wetherell + */ +public class BreadthFirstTraversal { + + @SuppressWarnings("unchecked") + public static final > Graph.Vertex[] breadthFirstTraversal(Graph graph, Graph.Vertex source) { + // use for look-up via index + final ArrayList> vertices = new ArrayList>(); + vertices.addAll(graph.getVertices()); + + // used for look-up via vertex + final int n = vertices.size(); + final Map,Integer> vertexToIndex = new HashMap,Integer>(); + for (int i=0; i v = vertices.get(i); + vertexToIndex.put(v,i); + } + + // adjacency matrix + final byte[][] adj = new byte[n][n]; + for (int i=0; i v = vertices.get(i); + final int idx = vertexToIndex.get(v); + final byte[] array = new byte[n]; + adj[idx] = array; + final List> edges = v.getEdges(); + for (Edge e : edges) + array[vertexToIndex.get(e.getToVertex())] = 1; + } + + // visited array + final byte[] visited = new byte[n]; + for (int i = 0; i < visited.length; i++) + visited[i] = -1; + + // for holding results + final Graph.Vertex[] arr = new Graph.Vertex[n]; + + // start at the source + Vertex element = source; + int c = 0; + int i = vertexToIndex.get(element); + int k = 0; + + arr[k] = element; + visited[i] = 1; + k++; + + final Queue> queue = new ArrayDeque>(); + queue.add(source); + while (!queue.isEmpty()) { + element = queue.peek(); + c = vertexToIndex.get(element); + i = 0; + while (i < n) { + if (adj[c][i] == 1 && visited[i] == -1) { + final Vertex v = vertices.get(i); + queue.add(v); + visited[i] = 1; + + arr[k] = v; + k++; + } + i++; + } + queue.poll(); + } + return arr; + } + + public static int[] breadthFirstTraversal(int n, byte[][] adjacencyMatrix, int source) { + final int[] visited = new int[n]; + for (int i = 0; i < visited.length; i++) + visited[i] = -1; + + int element = source; + int i = source; + int arr[] = new int[n]; + int k = 0; + + arr[k] = element; + visited[i] = 1; + k++; + + final Queue queue = new ArrayDeque(); + queue.add(source); + while (!queue.isEmpty()) { + element = queue.peek(); + i = 0; + while (i < n) { + if (adjacencyMatrix[element][i] == 1 && visited[i] == -1) { + queue.add(i); + visited[i] = 1; + + arr[k] = i; + k++; + } + i++; + } + queue.poll(); + } + return arr; + } +} + diff --git a/src/com/jwetherell/algorithms/graph/ConnectedComponents.java b/src/com/jwetherell/algorithms/graph/ConnectedComponents.java new file mode 100644 index 00000000..e096950c --- /dev/null +++ b/src/com/jwetherell/algorithms/graph/ConnectedComponents.java @@ -0,0 +1,74 @@ +package com.jwetherell.algorithms.graph; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.data_structures.Graph.Edge; +import com.jwetherell.algorithms.data_structures.Graph.Vertex; + +/** + * In graph theory, a connected component (or just component) of an undirected graph is a subgraph in which any two vertices are connected to each + * other by paths, and which is connected to no additional vertices in the supergraph. A vertex with no incident edges is itself a connected + * component. A graph that is itself connected has exactly one connected component, consisting of the whole graph. + *

+ * @see Connected Components (Wikipedia) + *
+ * @author Justin Wetherell + */ +public class ConnectedComponents { + + private ConnectedComponents() { } + + /** + * Finds the connected components subsets of the Graph. + * + * @param graph + * to find connected components. + * @return List of connected components in the Graph. + */ + public static final > List>> getConnectedComponents(Graph graph) { + if (graph == null) + throw new IllegalArgumentException("Graph is NULL."); + + if (graph.getType() != Graph.TYPE.DIRECTED) + throw new IllegalArgumentException("Cannot perform a connected components search on a non-directed graph. graph type = "+graph.getType()); + + final Map,Integer> map = new HashMap,Integer>(); + final List>> list = new ArrayList>>(); + + int c = 0; + for (Vertex v : graph.getVertices()) + if (map.get(v) == null) + visit(map, list, v, c++); + return list; + } + + private static final > void visit(Map,Integer> map, List>> list, Vertex v, int c) { + map.put(v, c); + + List> r = null; + if (c == list.size()) { + r = new ArrayList>(); + list.add(r); + } else { + r = list.get(c); + } + r.add(v); + + if (v.getEdges().size() > 0) { + boolean found = false; + for (Edge e : v.getEdges()) { + final Vertex to = e.getToVertex(); + if (map.get(to) == null) { + visit(map, list, to, c); + found = true; + } + if (found) + break; + } + } + } +} diff --git a/src/com/jwetherell/algorithms/graph/CycleDetection.java b/src/com/jwetherell/algorithms/graph/CycleDetection.java index 7d8c0369..d084f16c 100644 --- a/src/com/jwetherell/algorithms/graph/CycleDetection.java +++ b/src/com/jwetherell/algorithms/graph/CycleDetection.java @@ -6,44 +6,60 @@ import com.jwetherell.algorithms.data_structures.Graph; +/** + * In computer science, cycle detection or cycle finding is the algorithmic problem of finding a cycle in a sequence of iterated function values. + *

+ * @see Cycle Detection (Wikipedia) + *
+ * @author Justin Wetherell + */ public class CycleDetection { - private static Set> visitedVerticies = new HashSet>(); - private static Set> visitedEdges = new HashSet>(); + private CycleDetection() { } - private CycleDetection() { - }; + /** + * Cycle detection on a unidrected graph. + * + * @param graph Graph + * @return true if a cycle exists + */ + public static > boolean detect(Graph graph) { + if (graph == null) + throw new IllegalArgumentException("Graph is NULL."); - public static boolean detect(Graph g) { - if (g == null) - return false; - visitedVerticies.clear(); - visitedEdges.clear(); - List> verticies = g.getVerticies(); + if (graph.getType() != Graph.TYPE.UNDIRECTED) + throw new IllegalArgumentException("Graph is needs to be Undirected."); + + final Set> visitedVerticies = new HashSet>(); + final Set> visitedEdges = new HashSet>(); + + final List> verticies = graph.getVertices(); if (verticies == null || verticies.size() == 0) return false; // Select the zero-ith element as the root - Graph.Vertex root = verticies.get(0); - return depthFirstSearch(root); + final Graph.Vertex root = verticies.get(0); + return depthFirstSearch(root, visitedVerticies, visitedEdges); } - private static final boolean depthFirstSearch(Graph.Vertex vertex) { + private static final > boolean depthFirstSearch(Graph.Vertex vertex, Set> visitedVerticies, Set> visitedEdges) { if (!visitedVerticies.contains(vertex)) { - // Not visited + // Found an unvisited, add to the set visitedVerticies.add(vertex); - List> edges = vertex.getEdges(); + final List> edges = vertex.getEdges(); if (edges != null) { - for (Graph.Edge edge : edges) { - Graph.Vertex to = edge.getToVertex(); + // Follow each unvisited edge, visit the vertex the edge connects to. + for (Graph.Edge edge : edges) { + final Graph.Vertex to = edge.getToVertex(); boolean result = false; if (to != null && !visitedEdges.contains(edge)) { visitedEdges.add(edge); - Graph.Edge recip = new Graph.Edge(edge.getCost(), edge.getToVertex(), - edge.getFromVertex()); + + final Graph.Edge recip = new Graph.Edge(edge.getCost(), edge.getToVertex(), edge.getFromVertex()); visitedEdges.add(recip); - result = depthFirstSearch(to); + + result = depthFirstSearch(to, visitedVerticies, visitedEdges); } if (result == true) return result; diff --git a/src/com/jwetherell/algorithms/graph/DepthFirstTraversal.java b/src/com/jwetherell/algorithms/graph/DepthFirstTraversal.java new file mode 100644 index 00000000..76c5714b --- /dev/null +++ b/src/com/jwetherell/algorithms/graph/DepthFirstTraversal.java @@ -0,0 +1,131 @@ +package com.jwetherell.algorithms.graph; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.data_structures.Graph.Edge; +import com.jwetherell.algorithms.data_structures.Graph.Vertex; + +/** + * Depth-first search (DFS) is an algorithm for traversing or searching tree or graph data structures. One starts at the root (selecting some arbitrary node as the root in the case of a graph) and + * explores as far as possible along each branch before backtracking. + *

+ * @see Depth-First Search (Wikipedia) + *
+ * @author Justin Wetherell + */ +public class DepthFirstTraversal { + + @SuppressWarnings("unchecked") + public static > Graph.Vertex[] depthFirstTraversal(Graph graph, Graph.Vertex source) { + // use for look-up via index + final ArrayList> vertices = new ArrayList>(); + vertices.addAll(graph.getVertices()); + + // used for look-up via vertex + final int n = vertices.size(); + final Map,Integer> vertexToIndex = new HashMap,Integer>(); + for (int i=0; i v = vertices.get(i); + vertexToIndex.put(v,i); + } + + // adjacency matrix + final byte[][] adj = new byte[n][n]; + for (int i=0; i v = vertices.get(i); + final int idx = vertexToIndex.get(v); + final byte[] array = new byte[n]; + adj[idx] = array; + final List> edges = v.getEdges(); + for (Edge e : edges) + array[vertexToIndex.get(e.getToVertex())] = 1; + } + + // visited array + final byte[] visited = new byte[n]; + for (int i = 0; i < visited.length; i++) + visited[i] = -1; + + // for holding results + final Graph.Vertex[] arr = new Graph.Vertex[n]; + + // start at the source + Vertex element = source; + int c = 0; + int i = vertexToIndex.get(element); + int k = 0; + + visited[i] = 1; + arr[k] = element; + k++; + + final Stack> stack = new Stack>(); + stack.push(source); + while (!stack.isEmpty()) { + element = stack.peek(); + c = vertexToIndex.get(element); + i = 0; + while (i < n) { + if (adj[c][i] == 1 && visited[i] == -1) { + final Vertex v = vertices.get(i); + stack.push(v); + visited[i] = 1; + + element = v; + c = vertexToIndex.get(element); + i = 0; + + arr[k] = v; + k++; + continue; + } + i++; + } + stack.pop(); + } + return arr; + } + + public static int[] depthFirstTraversal(int n, byte[][] adjacencyMatrix, int source) { + final int[] visited = new int[n]; + for (int i = 0; i < visited.length; i++) + visited[i] = -1; + + int element = source; + int i = source; + int arr[] = new int[n]; + int k = 0; + + visited[source] = 1; + arr[k] = element; + k++; + + final Stack stack = new Stack(); + stack.push(source); + while (!stack.isEmpty()) { + element = stack.peek(); + i = 0; + while (i < n) { + if (adjacencyMatrix[element][i] == 1 && visited[i] == -1) { + stack.push(i); + visited[i] = 1; + + element = i; + i = 0; + + arr[k] = element; + k++; + continue; + } + i++; + } + stack.pop(); + } + return arr; + } +} diff --git a/src/com/jwetherell/algorithms/graph/Dijkstra.java b/src/com/jwetherell/algorithms/graph/Dijkstra.java index ba140ee6..929cded7 100644 --- a/src/com/jwetherell/algorithms/graph/Dijkstra.java +++ b/src/com/jwetherell/algorithms/graph/Dijkstra.java @@ -1,127 +1,139 @@ package com.jwetherell.algorithms.graph; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Queue; -import java.util.Set; -import java.util.TreeMap; import com.jwetherell.algorithms.data_structures.Graph; /** * Dijkstra's shortest path. Only works on non-negative path weights. Returns a * tuple of total cost of shortest path and the path. - * + *

* Worst case: O(|E| + |V| log |V|) - * + *

+ * @see Dijkstra's Algorithm (Wikipedia) + *
* @author Justin Wetherell */ public class Dijkstra { - private static Map, Graph.CostVertexPair> costs = null; - private static Map, Set>> paths = null; - private static Queue> unvisited = null; - private Dijkstra() { } - public static Map, Graph.CostPathPair> getShortestPaths(Graph g, Graph.Vertex start) { - getShortestPath(g, start, null); - Map, Graph.CostPathPair> map = new HashMap, Graph.CostPathPair>(); + public static Map, Graph.CostPathPair> getShortestPaths(Graph graph, Graph.Vertex start) { + final Map, List>> paths = new HashMap, List>>(); + final Map, Graph.CostVertexPair> costs = new HashMap, Graph.CostVertexPair>(); + + getShortestPath(graph, start, null, paths, costs); + + final Map, Graph.CostPathPair> map = new HashMap, Graph.CostPathPair>(); for (Graph.CostVertexPair pair : costs.values()) { int cost = pair.getCost(); Graph.Vertex vertex = pair.getVertex(); - Set> path = paths.get(vertex); + List> path = paths.get(vertex); map.put(vertex, new Graph.CostPathPair(cost, path)); } return map; } - public static Graph.CostPathPair getShortestPath(Graph g, Graph.Vertex start, Graph.Vertex end) { - if (g == null) + public static Graph.CostPathPair getShortestPath(Graph graph, Graph.Vertex start, Graph.Vertex end) { + if (graph == null) throw (new NullPointerException("Graph must be non-NULL.")); - // Reset variables - costs = null; - paths = null; - unvisited = null; + // Dijkstra's algorithm only works on positive cost graphs + final boolean hasNegativeEdge = checkForNegativeEdges(graph.getVertices()); + if (hasNegativeEdge) + throw (new IllegalArgumentException("Negative cost Edges are not allowed.")); + + final Map, List>> paths = new HashMap, List>>(); + final Map, Graph.CostVertexPair> costs = new HashMap, Graph.CostVertexPair>(); + return getShortestPath(graph, start, end, paths, costs); + } + + private static Graph.CostPathPair getShortestPath(Graph graph, + Graph.Vertex start, Graph.Vertex end, + Map, List>> paths, + Map, Graph.CostVertexPair> costs) { + if (graph == null) + throw (new NullPointerException("Graph must be non-NULL.")); + if (start == null) + throw (new NullPointerException("start must be non-NULL.")); // Dijkstra's algorithm only works on positive cost graphs - boolean hasNegativeEdge = checkForNegativeEdges(g.getVerticies()); + boolean hasNegativeEdge = checkForNegativeEdges(graph.getVertices()); if (hasNegativeEdge) throw (new IllegalArgumentException("Negative cost Edges are not allowed.")); - paths = new TreeMap, Set>>(); - for (Graph.Vertex v : g.getVerticies()) { - paths.put(v, new LinkedHashSet>()); - } + for (Graph.Vertex v : graph.getVertices()) + paths.put(v, new ArrayList>()); - costs = new TreeMap, Graph.CostVertexPair>(); - for (Graph.Vertex v : g.getVerticies()) { + for (Graph.Vertex v : graph.getVertices()) { if (v.equals(start)) costs.put(v, new Graph.CostVertexPair(0, v)); else costs.put(v, new Graph.CostVertexPair(Integer.MAX_VALUE, v)); } - unvisited = new PriorityQueue>(); - unvisited.addAll(costs.values()); // Shallow copy + final Queue> unvisited = new PriorityQueue>(); + unvisited.add(costs.get(start)); + + while (!unvisited.isEmpty()) { + final Graph.CostVertexPair pair = unvisited.remove(); + final Graph.Vertex vertex = pair.getVertex(); - Graph.Vertex vertex = start; - while (true) { - // Compute costs from current vertex to all reachable vertices which - // haven't been visited + // Compute costs from current vertex to all reachable vertices which haven't been visited for (Graph.Edge e : vertex.getEdges()) { - Graph.CostVertexPair pair = costs.get(e.getToVertex()); - Graph.CostVertexPair lowestCostToThisVertex = costs.get(vertex); - int cost = lowestCostToThisVertex.getCost() + e.getCost(); - if (pair.getCost() == Integer.MAX_VALUE) { + final Graph.CostVertexPair toPair = costs.get(e.getToVertex()); // O(1) + final Graph.CostVertexPair lowestCostToThisVertex = costs.get(vertex); // O(1) + final int cost = lowestCostToThisVertex.getCost() + e.getCost(); + if (toPair.getCost() == Integer.MAX_VALUE) { // Haven't seen this vertex yet - pair.setCost(cost); - Set> set = paths.get(e.getToVertex()); - set.addAll(paths.get(e.getFromVertex())); + + // Need to remove the pair and re-insert, so the priority queue keeps it's invariants + unvisited.remove(toPair); // O(n) + toPair.setCost(cost); + unvisited.add(toPair); // O(log n) + + // Update the paths + List> set = paths.get(e.getToVertex()); // O(log n) + set.addAll(paths.get(e.getFromVertex())); // O(log n) set.add(e); - } else if (cost < pair.getCost()) { + } else if (cost < toPair.getCost()) { // Found a shorter path to a reachable vertex - pair.setCost(cost); - Set> set = paths.get(e.getToVertex()); + + // Need to remove the pair and re-insert, so the priority queue keeps it's invariants + unvisited.remove(toPair); // O(n) + toPair.setCost(cost); + unvisited.add(toPair); // O(log n) + + // Update the paths + List> set = paths.get(e.getToVertex()); // O(log n) set.clear(); - set.addAll(paths.get(e.getFromVertex())); + set.addAll(paths.get(e.getFromVertex())); // O(log n) set.add(e); } } // Termination conditions if (end != null && vertex.equals(end)) { - // If we are looking for shortest path, we found it. - break; - } else if (unvisited.size() > 0) { - // If there are other vertices to visit (which haven't been - // visited yet) - Graph.CostVertexPair pair = unvisited.remove(); - vertex = pair.getVertex(); - if (pair.getCost() == Integer.MAX_VALUE) { - // If the only edge left to explore has MAX_VALUE then it - // cannot be reached from the starting vertex - break; - } - } else { - // No more edges to explore, we are done. + // We are looking for shortest path to a specific vertex, we found it. break; } } if (end != null) { - Graph.CostVertexPair pair = costs.get(end); - Set> set = paths.get(end); + final Graph.CostVertexPair pair = costs.get(end); + final List> set = paths.get(end); return (new Graph.CostPathPair(pair.getCost(), set)); } return null; } - private static boolean checkForNegativeEdges(List> vertitices) { + private static boolean checkForNegativeEdges(Collection> vertitices) { for (Graph.Vertex v : vertitices) { for (Graph.Edge e : v.getEdges()) { if (e.getCost() < 0) diff --git a/src/com/jwetherell/algorithms/graph/EdmondsKarp.java b/src/com/jwetherell/algorithms/graph/EdmondsKarp.java new file mode 100644 index 00000000..1431bbfa --- /dev/null +++ b/src/com/jwetherell/algorithms/graph/EdmondsKarp.java @@ -0,0 +1,90 @@ +package com.jwetherell.algorithms.graph; + + +import java.util.ArrayDeque; +import java.util.Queue; + +/** + * In computer science, the Edmonds–Karp algorithm is an implementation of the Ford–Fulkerson method for + * computing the maximum flow in a flow network in O(V*E^2) time. + *

+ * @see Edmonds-Karp Algorithm (Wikipedia) + *
+ * @author Mateusz Cianciara + * @author Justin Wetherell + */ +public class EdmondsKarp { + + private long[][] flow; //max flow beetween i and j verticles + private long[][] capacity; // edge capacity + private int[] parent; //parent + private boolean[] visited; //just for checking if visited + @SuppressWarnings("unused") + private int n, m; + + public EdmondsKarp(int numOfVerticles, int numOfEdges) { + this.n = numOfVerticles; + this.m = numOfEdges; + this.flow = new long[n][n]; + this.capacity = new long[n][n]; + this.parent = new int[n]; + this.visited = new boolean[n]; + } + + public void addEdge(int from, int to, long capacity) { + assert capacity >= 0; + this.capacity[from][to] += capacity; + } + + /** + * Get maximum flow. + * + * @param s source + * @param t target + * @return maximum flow + */ + public long getMaxFlow(int s, int t) { + while (true) { + final Queue Q = new ArrayDeque(); + Q.add(s); + + for (int i = 0; i < this.n; ++i) + visited[i] = false; + visited[s] = true; + + boolean check = false; + int current; + while (!Q.isEmpty()) { + current = Q.peek(); + if (current == t) { + check = true; + break; + } + Q.remove(); + for (int i = 0; i < n; ++i) { + if (!visited[i] && capacity[current][i] > flow[current][i]) { + visited[i] = true; + Q.add(i); + parent[i] = current; + } + } + } + if (check == false) + break; + + long temp = capacity[parent[t]][t] - flow[parent[t]][t]; + for (int i = t; i != s; i = parent[i]) + temp = Math.min(temp, (capacity[parent[i]][i] - flow[parent[i]][i])); + + for (int i = t; i != s; i = parent[i]) { + flow[parent[i]][i] += temp; + flow[i][parent[i]] -= temp; + } + } + + long result = 0; + for (int i = 0; i < n; ++i) + result += flow[s][i]; + return result; + } +} diff --git a/src/com/jwetherell/algorithms/graph/FloydWarshall.java b/src/com/jwetherell/algorithms/graph/FloydWarshall.java index 10e45b13..b9251df6 100644 --- a/src/com/jwetherell/algorithms/graph/FloydWarshall.java +++ b/src/com/jwetherell/algorithms/graph/FloydWarshall.java @@ -9,33 +9,34 @@ /** * Floyd–Warshall algorithm is a graph analysis algorithm for finding shortest * paths in a weighted graph (with positive or negative edge weights). - * + *

* Worst case: O(V^3) - * + *

+ * @see Floyd-Warshall Algorithm (Wikipedia) + *
* @author Justin Wetherell */ public class FloydWarshall { - private FloydWarshall() { - } + private FloydWarshall() { } - public static Map, Map, Integer>> getAllPairsShortestPaths( - Graph g) { - Map, Map, Integer>> allShortestPaths = new HashMap, Map, Integer>>(); + public static Map, Map, Integer>> getAllPairsShortestPaths(Graph graph) { + if (graph == null) + throw (new NullPointerException("Graph must be non-NULL.")); - List> vertices = g.getVerticies(); - int[][] sums = new int[vertices.size()][vertices.size()]; + final List> vertices = graph.getVertices(); + final int[][] sums = new int[vertices.size()][vertices.size()]; for (int i = 0; i < sums.length; i++) { for (int j = 0; j < sums[i].length; j++) { sums[i][j] = Integer.MAX_VALUE; } } - List> edges = g.getEdges(); + final List> edges = graph.getEdges(); for (Graph.Edge e : edges) { - int indexOfFrom = vertices.indexOf(e.getFromVertex()); - int indexOfTo = vertices.indexOf(e.getToVertex()); + final int indexOfFrom = vertices.indexOf(e.getFromVertex()); + final int indexOfTo = vertices.indexOf(e.getToVertex()); sums[indexOfFrom][indexOfTo] = e.getCost(); } @@ -45,11 +46,14 @@ public static Map, Map, Integer>> ge if (i == j) { sums[i][j] = 0; } else { - int ijCost = sums[i][j]; - int ikCost = sums[i][k]; - int kjCost = sums[k][j]; - int summed = (ikCost != Integer.MAX_VALUE && kjCost != Integer.MAX_VALUE) ? (ikCost + kjCost) - : Integer.MAX_VALUE; + final int ijCost = sums[i][j]; + final int ikCost = sums[i][k]; + final int kjCost = sums[k][j]; + final int summed = (ikCost != Integer.MAX_VALUE && + kjCost != Integer.MAX_VALUE) ? + (ikCost + kjCost) + : + Integer.MAX_VALUE; if (ijCost > summed) sums[i][j] = summed; } @@ -57,20 +61,22 @@ public static Map, Map, Integer>> ge } } + final Map, Map, Integer>> allShortestPaths = new HashMap, Map, Integer>>(); for (int i = 0; i < sums.length; i++) { for (int j = 0; j < sums[i].length; j++) { - Graph.Vertex from = vertices.get(i); - Graph.Vertex to = vertices.get(j); + final Graph.Vertex from = vertices.get(i); + final Graph.Vertex to = vertices.get(j); + Map, Integer> map = allShortestPaths.get(from); if (map == null) map = new HashMap, Integer>(); - int cost = sums[i][j]; + + final int cost = sums[i][j]; if (cost != Integer.MAX_VALUE) map.put(to, cost); allShortestPaths.put(from, map); } } - return allShortestPaths; } } diff --git a/src/com/jwetherell/algorithms/graph/Johnson.java b/src/com/jwetherell/algorithms/graph/Johnson.java index 721e18d4..5981b057 100644 --- a/src/com/jwetherell/algorithms/graph/Johnson.java +++ b/src/com/jwetherell/algorithms/graph/Johnson.java @@ -1,8 +1,8 @@ package com.jwetherell.algorithms.graph; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Set; import com.jwetherell.algorithms.data_structures.Graph; @@ -10,68 +10,72 @@ * Johnson's algorithm is a way to find the shortest paths between all pairs of * vertices in a sparse directed graph. It allows some of the edge weights to be * negative numbers, but no negative-weight cycles may exist. - * + *

* Worst case: O(V^2 log V + VE) - * + *

+ * @see Johnson's Algorithm (Wikipedia) + *
* @author Justin Wetherell */ public class Johnson { - private Johnson() { - } + private Johnson() { } + + public static Map, Map, List>>> getAllPairsShortestPaths(Graph g) { + if (g == null) + throw (new NullPointerException("Graph must be non-NULL.")); - public static Map, Map, Set>>> getAllPairsShortestPaths( - Graph g) { - Map, Map, Set>>> allShortestPaths = new HashMap, Map, Set>>>(); + // First, a new node 'connector' is added to the graph, connected by zero-weight edges to each of the other nodes. + final Graph graph = new Graph(g); + final Graph.Vertex connector = new Graph.Vertex(Integer.MAX_VALUE); // Add the connector Vertex to all edges. - for (Graph.Vertex v : g.getVerticies()) { - Graph graph = new Graph(g); // Clone the original - // graph - Graph.Vertex connector = new Graph.Vertex(Integer.MAX_VALUE); // Make - // new - // Vertex - // that - // connects - // to - // all - // Vertices - graph.getVerticies().add(connector); + for (Graph.Vertex v : graph.getVertices()) { + final int indexOfV = graph.getVertices().indexOf(v); + final Graph.Edge edge = new Graph.Edge(0, connector, graph.getVertices().get(indexOfV)); + connector.addEdge(edge); + graph.getEdges().add(edge); + } - int indexOfV = graph.getVerticies().indexOf(v); - Graph.Edge e = new Graph.Edge(0, connector, graph.getVerticies().get(indexOfV)); - connector.addEdge(e); - graph.getEdges().add(e); + graph.getVertices().add(connector); - Map, Graph.CostPathPair> costs = BellmanFord.getShortestPaths(graph, - connector); - if (BellmanFord.containsNegativeWeightCycle()) { - System.out.println("Graph contains a negative weight cycle. Cannot compute shortest path."); - return null; - } - for (Graph.Vertex v2 : costs.keySet()) { - int index = graph.getVerticies().indexOf(v2); - Graph.Vertex vertexToAdjust = graph.getVerticies().get(index); - Graph.CostPathPair pair = costs.get(v2); - vertexToAdjust.setWeight(pair.getCost()); - } + // Second, the Bellman–Ford algorithm is used, starting from the new vertex 'connector', to find for each vertex 'v' + // the minimum weight h(v) of a path from 'connector' to 'v'. If this step detects a negative cycle, the algorithm is terminated. + final Map, Graph.CostPathPair> costs = BellmanFord.getShortestPaths(graph, connector); - for (Graph.Edge e2 : graph.getEdges()) { - int startCost = e2.getFromVertex().getWeight(); - int endCode = e2.getToVertex().getWeight(); - int adjCost = e2.getCost() + startCost - endCode; - e2.setCost(adjCost); - } + // Next the edges of the original graph are re-weighted using the values computed by the Bellman–Ford algorithm: an edge + // from u to v, having length w(u,v), is given the new length w(u,v) + h(u) − h(v). + for (Graph.Edge e : graph.getEdges()) { + final int weight = e.getCost(); + final Graph.Vertex u = e.getFromVertex(); + final Graph.Vertex v = e.getToVertex(); + + // Don't worry about the connector + if (u.equals(connector) || v.equals(connector)) + continue; - int index = graph.getVerticies().indexOf(connector); - graph.getVerticies().remove(index); - index = graph.getEdges().indexOf(e); - graph.getEdges().remove(index); + // Adjust the costs + final int uCost = costs.get(u).getCost(); + final int vCost = costs.get(v).getCost(); + final int newWeight = weight + uCost - vCost; + e.setCost(newWeight); + } + + // Finally, 'connector' is removed, and Dijkstra's algorithm is used to find the shortest paths from each node (s) to every + // other vertex in the re-weighted graph. + final int indexOfConnector = graph.getVertices().indexOf(connector); + graph.getVertices().remove(indexOfConnector); + for (Graph.Edge e : connector.getEdges()) { + final int indexOfConnectorEdge = graph.getEdges().indexOf(e); + graph.getEdges().remove(indexOfConnectorEdge); + } - Map, Graph.CostPathPair> costPaths = Dijkstra.getShortestPaths(graph, v); - Map, Set>> paths = new HashMap, Set>>(); + final Map, Map, List>>> allShortestPaths = new HashMap, Map, List>>>(); + for (Graph.Vertex v : graph.getVertices()) { + final Map, Graph.CostPathPair> costPaths = Dijkstra.getShortestPaths(graph, v); + final Map, List>> paths = new HashMap, List>>(); for (Graph.Vertex v2 : costPaths.keySet()) { - Graph.CostPathPair pair = costPaths.get(v2); + final Graph.CostPathPair pair = costPaths.get(v2); paths.put(v2, pair.getPath()); } allShortestPaths.put(v, paths); diff --git a/src/com/jwetherell/algorithms/graph/Kruskal.java b/src/com/jwetherell/algorithms/graph/Kruskal.java new file mode 100644 index 00000000..db21ee80 --- /dev/null +++ b/src/com/jwetherell/algorithms/graph/Kruskal.java @@ -0,0 +1,82 @@ +package com.jwetherell.algorithms.graph; + +import com.jwetherell.algorithms.data_structures.Graph; + +import java.util.*; + +/** + * Kruskal's minimum spanning tree. Only works on undirected graphs. It finds a + * subset of the edges that forms a tree that includes every vertex, where the + * total weight of all the edges in the tree is minimized. + *

+ * @see Kruskal's Algorithm (Wikipedia) + *
+ * @author Bartlomiej Drozd + * @author Justin Wetherell + */ +public class Kruskal { + + private Kruskal() { } + + public static Graph.CostPathPair getMinimumSpanningTree(Graph graph) { + if (graph == null) + throw (new NullPointerException("Graph must be non-NULL.")); + + // Kruskal's algorithm only works on undirected graphs + if (graph.getType() == Graph.TYPE.DIRECTED) + throw (new IllegalArgumentException("Undirected graphs only.")); + + int cost = 0; + final List> path = new ArrayList>(); + + // Prepare data to store information which part of tree given vertex is + HashMap, HashSet>> membershipMap = new HashMap, HashSet>>(); + for (Graph.Vertex v : graph.getVertices()) { + HashSet> set = new HashSet>(); + set.add(v); + membershipMap.put(v, set); + } + + // We make queue of edges to consider all of them, starting with edge with the lowest cost, + // it is important that Edge's class comparator is not natural (ex. sorting is from the biggest to the lowest) + PriorityQueue> edgeQueue = new PriorityQueue>(graph.getEdges()); + + while (!edgeQueue.isEmpty()) { + Graph.Edge edge = edgeQueue.poll(); + + // If from vertex and to vertex are from different parts of tree then add this edge to result and union vertices' parts + if (!isTheSamePart(edge.getFromVertex(), edge.getToVertex(), membershipMap)) { + union(edge.getFromVertex(), edge.getToVertex(), membershipMap); + path.add(edge); + cost += edge.getCost(); + } + } + + + return (new Graph.CostPathPair(cost, path)); + } + + private static boolean isTheSamePart(Graph.Vertex v1, Graph.Vertex v2, HashMap, HashSet>> membershipMap) { + return membershipMap.get(v1) == membershipMap.get(v2); + } + + private static void union(Graph.Vertex v1, Graph.Vertex v2, HashMap, HashSet>> membershipMap) { + HashSet> firstSet = membershipMap.get(v1); //first set is the bigger set + HashSet> secondSet = membershipMap.get(v2); + + // we want to include smaller set into bigger, so second set cannot be bigger than first + if (secondSet.size() > firstSet.size()) { + HashSet> tempSet = firstSet; + firstSet = secondSet; + secondSet = tempSet; + } + + // changing part membership of each vertex from smaller set + for (Graph.Vertex v : secondSet) { + membershipMap.put(v, firstSet); + } + + // adding all vertices from smaller set to bigger one + firstSet.addAll(secondSet); + } +} diff --git a/src/com/jwetherell/algorithms/graph/Prim.java b/src/com/jwetherell/algorithms/graph/Prim.java index c440525c..e73724ca 100644 --- a/src/com/jwetherell/algorithms/graph/Prim.java +++ b/src/com/jwetherell/algorithms/graph/Prim.java @@ -1,7 +1,7 @@ package com.jwetherell.algorithms.graph; import java.util.ArrayList; -import java.util.LinkedHashSet; +import java.util.HashSet; import java.util.List; import java.util.PriorityQueue; import java.util.Queue; @@ -13,52 +13,47 @@ * Prim's minimum spanning tree. Only works on undirected graphs. It finds a * subset of the edges that forms a tree that includes every vertex, where the * total weight of all the edges in the tree is minimized. - * + *

+ * @see Prim's Minimum Spanning Tree (Wikipedia) + *
* @author Justin Wetherell */ public class Prim { - private static int cost = 0; - private static Set> path = null; - private static List> unvisited = null; - private static Queue> edgesAvailable = null; - private Prim() { } - public static Graph.CostPathPair getMinimumSpanningTree(Graph g, Graph.Vertex start) { - if (g == null) + public static Graph.CostPathPair getMinimumSpanningTree(Graph graph, Graph.Vertex start) { + if (graph == null) throw (new NullPointerException("Graph must be non-NULL.")); - // Reset variables - cost = 0; - path = null; - unvisited = null; - edgesAvailable = null; - // Prim's algorithm only works on undirected graphs - if (g.getType() == Graph.TYPE.DIRECTED) throw (new IllegalArgumentException("Undirected graphs only.")); + if (graph.getType() == Graph.TYPE.DIRECTED) + throw (new IllegalArgumentException("Undirected graphs only.")); - path = new LinkedHashSet>(); + int cost = 0; - unvisited = new ArrayList>(); - unvisited.addAll(g.getVerticies()); - unvisited.remove(start); + final Set> unvisited = new HashSet>(); + unvisited.addAll(graph.getVertices()); + unvisited.remove(start); // O(1) - edgesAvailable = new PriorityQueue>(); + final List> path = new ArrayList>(); + final Queue> edgesAvailable = new PriorityQueue>(); Graph.Vertex vertex = start; while (!unvisited.isEmpty()) { + // Add all edges to unvisited vertices for (Graph.Edge e : vertex.getEdges()) { if (unvisited.contains(e.getToVertex())) edgesAvailable.add(e); } - Graph.Edge e = edgesAvailable.remove(); + // Remove the lowest cost edge + final Graph.Edge e = edgesAvailable.remove(); cost += e.getCost(); - path.add(e); + path.add(e); // O(1) vertex = e.getToVertex(); - unvisited.remove(vertex); + unvisited.remove(vertex); // O(1) } return (new Graph.CostPathPair(cost, path)); diff --git a/src/com/jwetherell/algorithms/graph/PushRelabel.java b/src/com/jwetherell/algorithms/graph/PushRelabel.java new file mode 100644 index 00000000..fa5c03fa --- /dev/null +++ b/src/com/jwetherell/algorithms/graph/PushRelabel.java @@ -0,0 +1,250 @@ +package com.jwetherell.algorithms.graph; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.TreeMap; + +import com.jwetherell.algorithms.data_structures.Graph; + +/** + * In mathematical optimization, the push–relabel algorithm (alternatively, preflow–push + * algorithm) is an algorithm for computing maximum flows. The name "push–relabel" comes + * from the two basic operations used in the algorithm. Throughout its execution, the + * algorithm maintains a "preflow" and gradually converts it into a maximum flow by moving + * flow locally between neighboring nodes using push operations under the guidance of an + * admissible network maintained by relabel operations. + *

+ * @see Push-Relabel Algorithm (Wikipedia) + *
+ * @author Miron Ficak + * @author Justin Wetherell + */ +public class PushRelabel { + + private final Queue queue = new ArrayDeque(); + private final List vertices = new ArrayList(); + + private int relabelCounter; + private int n; + private Vertex source; + private Vertex sink; + + /** + * Computes maximum flow in flow network, using push-relabel algorithm with O(V^3) complexity. + * + * @param edgesToCapacities represents edges of network with capacities + * @param source source of network + * @param sink sink of network + * @param parameter of graph on which network is based + * @return the maximum flow + */ + public static > Long getMaximumFlow(Map, Long> edgesToCapacities, Graph.Vertex source, Graph.Vertex sink) { + if (edgesToCapacities == null) + throw new IllegalArgumentException("Graph is NULL."); + + final Map, Vertex> vertexMap = new TreeMap, Vertex>(); + for (Graph.Edge edge : edgesToCapacities.keySet()) { + vertexMap.put(edge.getFromVertex(), new Vertex()); + vertexMap.put(edge.getToVertex(), new Vertex()); + } + + final Vertex s = new Vertex(); // source + vertexMap.put(source, s); + + final Vertex t = new Vertex(); // sink + vertexMap.put(sink, t); + + final PushRelabel pushRelabel = new PushRelabel(vertexMap.values(), s, t); + for (Map.Entry, Long> edgeWithCapacity : edgesToCapacities.entrySet()) { + final Graph.Edge e = edgeWithCapacity.getKey(); + addEdge( + vertexMap.get(e.getFromVertex()), + vertexMap.get(e.getToVertex()), + edgeWithCapacity.getValue() + ); + } + + return pushRelabel.maxFlow(); + } + + private PushRelabel(Collection vertices, Vertex source, Vertex sink) { + this.vertices.addAll(vertices); + this.source = source; + this.sink = sink; + this.n = vertices.size(); + } + + private static final void addEdge(Vertex from, Vertex to, long cost) { + final int placeOfEdge = from.edges.indexOf(new Edge(from, to)); + if (placeOfEdge == -1) { + final Edge edge = new Edge(from, to, cost); + final Edge revertedEdge = new Edge(to, from, 0); + edge.revertedEdge = revertedEdge; + revertedEdge.revertedEdge = edge; + from.edges.add(edge); + to.edges.add(revertedEdge); + } else { + from.edges.get(placeOfEdge).cost += cost; + } + } + + private final void recomputeHeight() { + final Queue que = new ArrayDeque(); + for (Vertex vertex : vertices) { + vertex.visited = false; + vertex.height = 2 * n; + } + + sink.height = 0; + source.height = n; + source.visited = true; + sink.visited = true; + que.add(sink); + while (!que.isEmpty()) { + final Vertex act = que.poll(); + for (Edge e : act.edges) { + if (!e.to.visited && e.revertedEdge.cost > e.revertedEdge.flow) { + e.to.height = act.height + 1; + que.add(e.to); + e.to.visited = true; + } + } + } + que.add(source); + while (!que.isEmpty()) { + final Vertex act = que.poll(); + for (Edge e : act.edges) { + if (!e.to.visited && e.revertedEdge.cost > e.revertedEdge.flow) { + e.to.height = act.height + 1; + que.add(e.to); + e.to.visited = true; + } + } + } + } + + private final void init() { + for (Edge e : source.edges) { + e.flow = e.cost; + e.revertedEdge.flow = -e.flow; + e.to.excess += e.flow; + if (e.to != source && e.to != sink) + queue.add(e.to); + } + recomputeHeight(); + relabelCounter = 0; + } + + private static final void relabel(Vertex v) { + int minimum = 0; + for (Edge e : v.edges) { + if (e.flow < e.cost) + minimum = Math.min(minimum, e.to.height); + } + v.height = minimum + 1; + } + + private final void push(Vertex u, Edge e) { + final long delta = (u.excess < e.cost - e.flow) ? u.excess : e.cost - e.flow; + e.flow += delta; + e.revertedEdge.flow -= delta; + u.excess -= delta; + + if (e.to.excess == 0 && e.to != source && e.to != sink) + queue.add(e.to); + + e.to.excess += delta; + } + + private final void discharge(Vertex u) { + while (u.excess > 0) { + if (u.currentEdge == u.edges.size()) { + relabel(u); + if ((++relabelCounter) == n) { + recomputeHeight(); + for (Vertex vertex : vertices) + vertex.currentEdge = 0; + relabelCounter = 0; + } + u.currentEdge = 0; + } else { + Edge e = u.edges.get(u.currentEdge); + if (e.flow < e.cost && u.height == e.to.height + 1) + push(u, e); + else + u.currentEdge++; + } + } + } + + private final long maxFlow() { + init(); + while (!queue.isEmpty()) + discharge(queue.poll()); + return sink.excess; + } + + private static final class Vertex { + + private final List edges = new ArrayList(); + + private boolean visited = false; + private int height; + private int currentEdge; + private long excess; + + } + + private final static class Edge { + + private final Vertex from; + private final Vertex to; + private long cost; + + private long flow; + private Edge revertedEdge; + + private Edge(Vertex from, Vertex to, long cost) { + this.from = from; + this.to = to; + this.cost = cost; + } + + private Edge(Vertex from, Vertex to) { + this.from = from; + this.to = to; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + final Edge edge = (Edge) o; + if (!from.equals(edge.from)) + return false; + return to.equals(edge.to); + + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int result = from.hashCode(); + result = 31 * result + to.hashCode(); + return result; + } + } +} diff --git a/src/com/jwetherell/algorithms/graph/TopologicalSort.java b/src/com/jwetherell/algorithms/graph/TopologicalSort.java index 2b7cc49c..20a2f551 100644 --- a/src/com/jwetherell/algorithms/graph/TopologicalSort.java +++ b/src/com/jwetherell/algorithms/graph/TopologicalSort.java @@ -9,38 +9,71 @@ * In computer science, a topological sort (sometimes abbreviated topsort or * toposort) or topological ordering of a directed graph is a linear ordering of * its vertices such that, for every edge uv, u comes before v in the ordering. - * + *

+ * @see Topological Sort (Wikipedia) + *
* @author Justin Wetherell */ public class TopologicalSort { - private TopologicalSort() { - }; + private TopologicalSort() { } + /** + * Performs a topological sort on a directed graph. Returns NULL if a cycle is detected. + * + * Note: This should NOT change the state of the graph parameter. + * + * @param graph + * @return Sorted List of Vertices or NULL if graph has a cycle + */ public static final List> sort(Graph graph) { - List> sorted = new ArrayList>(); - List> noOutgoing = new ArrayList>(); - for (Graph.Vertex v : graph.getVerticies()) { - if (v.getEdges().size() == 0) { + if (graph == null) + throw new IllegalArgumentException("Graph is NULL."); + + if (graph.getType() != Graph.TYPE.DIRECTED) + throw new IllegalArgumentException("Cannot perform a topological sort on a non-directed graph. graph type = "+graph.getType()); + + // clone to prevent changes the graph parameter's state + final Graph clone = new Graph(graph); + final List> sorted = new ArrayList>(); + final List> noOutgoing = new ArrayList>(); + + final List> edges = new ArrayList>(); + edges.addAll(clone.getEdges()); + + // Find all the vertices which have no outgoing edges + for (Graph.Vertex v : clone.getVertices()) { + if (v.getEdges().size() == 0) noOutgoing.add(v); - } } + + // While we still have vertices which have no outgoing edges while (noOutgoing.size() > 0) { - Graph.Vertex v = noOutgoing.remove(0); - sorted.add(v); - for (Graph.Edge e : graph.getEdges()) { - Graph.Vertex v2 = e.getFromVertex(); - Graph.Vertex v3 = e.getToVertex(); - if (v3.equals(v)) { - graph.getEdges().remove(e); - v2.getEdges().remove(e); + final Graph.Vertex current = noOutgoing.remove(0); + sorted.add(current); + + // Go thru each edge, if it goes to the current vertex then remove it. + int i = 0; + while (i < edges.size()) { + final Graph.Edge e = edges.get(i); + final Graph.Vertex from = e.getFromVertex(); + final Graph.Vertex to = e.getToVertex(); + // Found an edge to the current vertex, remove it. + if (to.equals(current)) { + edges.remove(e); + // Remove the reciprocal edge + from.getEdges().remove(e); + } else { + i++; } - if (v2.getEdges().size() == 0) - noOutgoing.add(v2); + // Removed all edges from 'from' vertex, add it to the onOutgoing list + if (from.getEdges().size() == 0) + noOutgoing.add(from); } } - if (graph.getEdges().size() > 0) - System.out.println("cycle detected"); + // If we have processed all connected vertices and there are edges remaining, graph has multiple connected components. + if (edges.size() > 0) + return null; return sorted; } } diff --git a/src/com/jwetherell/algorithms/graph/TurboMatching.java b/src/com/jwetherell/algorithms/graph/TurboMatching.java new file mode 100644 index 00000000..aabaf1ba --- /dev/null +++ b/src/com/jwetherell/algorithms/graph/TurboMatching.java @@ -0,0 +1,110 @@ +package com.jwetherell.algorithms.graph; + +import com.jwetherell.algorithms.data_structures.Graph; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * In the mathematical discipline of graph theory, a matching or independent edge set + * in a graph is a set of edges without common vertices. In some matchings, all the vertices + * may incident with some edge of the matching, but this is not required and can only occur + * if the number of vertices is even. + *

+ * @see Matching (Wikipedia) + *
+ * @author Jakub Szarawarski + * @author Justin Wetherell + */ +public class TurboMatching { + + /** + * Computes maximum matching, using turbomatching algorithm based on augmenting paths with O(EV) complexity. + * + * @param graph bipartite graph + * @param parameter of graph on which network is based + * @return a MatchingResult class instance containg a map of mates for each paired vertex and number of pairs + */ + public static > MatchingResult getMaximumMatching(Graph graph){ + final Map, Graph.Vertex> mate = new HashMap, Graph.Vertex>(); + + while (pathset(graph, mate)); + + return new MatchingResult(mate); + } + + /** + * Searches for an augmenting path for each unmatched vertex. + * + * @param graph bipartite graph + * @param mate map containing a mate for each matched vertex + * @return information if any augmenting path was found + */ + private static > boolean pathset(Graph graph, Map, Graph.Vertex> mate){ + final Set> visited = new HashSet>(); + + boolean result = false; + for (Graph.Vertex vertex : graph.getVertices()) { + if (mate.containsKey(vertex) == false) { + if (path(graph, mate, visited, vertex)) + result = true; + } + } + return result; + } + + /** + * Searches for an augmenting path for a vertex. + * Refreshes mates map appropriately. + * + * @param graph bipartite graph + * @param mate map containing a mate for each matched vertex + * @param visited set containing vertices visited in current pathset + * @param vertex regarded vertex + * @param parameter of graph on which network is based + * @return information if an augmenting path was found + */ + private static > boolean path(Graph graph, Map, Graph.Vertex> mate, Set> visited, Graph.Vertex vertex){ + if (visited.contains(vertex)) + return false; + + visited.add(vertex); + for (Graph.Edge edge : vertex.getEdges()) { + final Graph.Vertex neighbour = edge.getFromVertex().equals(vertex) ? edge.getToVertex() : edge.getFromVertex(); + if (mate.containsKey(neighbour) == false || path(graph, mate, visited, mate.get(neighbour))) { + mate.put(vertex, neighbour); + mate.put(neighbour, vertex); + return true; + } + } + return false; + } + + + public static class MatchingResult>{ + + private final Map, Graph.Vertex> mate; + private final int size; + + private MatchingResult(Map, Graph.Vertex> mate){ + this.mate = mate; + this.size = mate.size()/2; + } + + /** + * @return the number of edges in independent edge set + */ + public int getSize(){ + return this.size; + } + + /** + * @return a symetric map that contains a mate for each matched vertex + */ + public Map, Graph.Vertex> getMate(){ + return this.mate; + } + } +} diff --git a/src/com/jwetherell/algorithms/mathematics/Coprimes.java b/src/com/jwetherell/algorithms/mathematics/Coprimes.java new file mode 100644 index 00000000..73bf8288 --- /dev/null +++ b/src/com/jwetherell/algorithms/mathematics/Coprimes.java @@ -0,0 +1,42 @@ +package com.jwetherell.algorithms.mathematics; + +/** + * In number theory, two integers a and b are said to be relatively prime, mutually prime, or coprime (also spelled + * co-prime) if the only positive integer that divides both of them is 1. That is, the only common positive factor + * of the two numbers is 1. This is equivalent to their greatest common divisor being 1. + *

+ * @see Mutually Prime / Co-prime (Wikipedia) + *
+ * @author Szymon Stankiewicz + * @author Justin Wetherell + */ +public class Coprimes { + + private Coprimes() { } + + /** + * + * Euler's totient function. Because this function is multiplicative such implementation is possible. + *

+ * Time complexity: O(sqrt(n)) + *

+ * @param n Long integer + * @return number of coprimes smaller or equal to n + */ + public static long getNumberOfCoprimes(long n) { + if(n < 1) + return 0; + long res = 1; + for(int i = 2; i*i <= n; i++) { + int times = 0; + while(n%i == 0) { + res *= (times > 0 ? i : i-1); + n /= i; + times++; + } + } + if(n > 1) + res *= n-1; + return res; + } +} diff --git a/src/com/jwetherell/algorithms/mathematics/DiscreteLogarithm.java b/src/com/jwetherell/algorithms/mathematics/DiscreteLogarithm.java new file mode 100644 index 00000000..f3d470d6 --- /dev/null +++ b/src/com/jwetherell/algorithms/mathematics/DiscreteLogarithm.java @@ -0,0 +1,70 @@ +package com.jwetherell.algorithms.mathematics; + +import java.util.HashMap; + +import static java.lang.Math.sqrt; + +/** + * In mathematics, a discrete logarithm is an integer k exponent solving the equation bk = g, where b and g are + * elements of a group. Discrete logarithms are thus the group-theoretic analogue of ordinary logarithms, which + * solve the same equation for real numbers b and g, where b is the base of the logarithm and g is the value whose + * logarithm is being taken. + *

+ * @see Discrete Logarithm (Wikipedia) + *
+ * @author Lucjan Rosłanowski + * @author Justin Wetherell + */ +public class DiscreteLogarithm { + + public static final long NO_SOLUTION = -1; + + private static final HashMap set = new HashMap(); + + private DiscreteLogarithm() { } + + private static final long pow(long a, long x, long p) { + if (x == 0) + return 1; + + if (x == 1) + return a % p; + + if (x % 2 != 0) + return (a * pow(a, x - 1, p)) % p; + + final long temp = pow(a, x / 2, p) % p; + return (temp * temp) % p; + } + + private static final long getDiscreteLogarithm(HashMap set, long s, long a, long p) { + for (long i = 0; i < s; ++i) { + long el = pow(a, (i * s) % p, p); + el = pow(el, p - 2, p); + + if (set.containsKey(el)) + return i * s + set.get(el); + } + return NO_SOLUTION; + } + + private static final void generateSet(long a, long b_1, long p, long s, HashMap set) { + set.clear(); + for (long i = 0; i < s; ++i) { + final long first = (pow(a, i, p) * b_1) % p; + if (!set.containsKey(first)) + set.put(first, i); + } + } + + /** + * Returns DiscreteLogarithm.NO_SOLUTION when a solution cannot be found + */ + public static final long countDiscreteLogarithm(final long a, final long b, final long p) { + final long s = (long) sqrt(p) + 1; + final long b_1 = pow(b, p - 2, p); + + generateSet(a, b_1, p, s, set); + return getDiscreteLogarithm(set, s,a,p); + } +} diff --git a/src/com/jwetherell/algorithms/mathematics/Distance.java b/src/com/jwetherell/algorithms/mathematics/Distance.java index 8a4b310e..ef8e47ee 100644 --- a/src/com/jwetherell/algorithms/mathematics/Distance.java +++ b/src/com/jwetherell/algorithms/mathematics/Distance.java @@ -2,8 +2,7 @@ public class Distance { - private Distance() { - } + private Distance() { } /* * Chess distance @@ -13,7 +12,7 @@ public static final long chebyshevDistance(long[] point1, long[] point2) { long y1 = point1[1]; long x2 = point2[0]; long y2 = point2[1]; - return (int) Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2)); + return Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2)); } public static final double squaredDistance(double x1, double y1, double x2, double y2) { diff --git a/src/com/jwetherell/algorithms/mathematics/Exponentiation.java b/src/com/jwetherell/algorithms/mathematics/Exponentiation.java new file mode 100644 index 00000000..b5746a77 --- /dev/null +++ b/src/com/jwetherell/algorithms/mathematics/Exponentiation.java @@ -0,0 +1,60 @@ +package com.jwetherell.algorithms.mathematics; + +/** + * Recursive function of exponentiation is just an implementation of definition. + *

+ * @see Exponentiation (Wikipedia) + *

+ * Complexity - O(N) where N is exponent. + *

+ * Fast exponentiation's complexity is O(lg N) + *

+ * @see Exponentiation by Squaring (Wikipedia) + *
+ * Modular exponentiation is similar. + *

+ * @see Modular Exponentiation (Wikipedia) + *

+ * This implementation is the fast version of this algorithm with a complexity of O(lg N) also + *
+ * @author Bartlomiej Drozd + * @author Justin Wetherell + */ +public class Exponentiation { + + public static int recursiveExponentiation(int base, int exponent) { + if (exponent == 0) + return 1; + if (exponent == 1) + return base; + + return recursiveExponentiation(base, exponent - 1) * base; + } + + public static int fastRecursiveExponentiation(int base, int exponent) { + if (exponent == 0) + return 1; + if (exponent == 1) + return base; + + final int resultOnHalfExponent = fastRecursiveExponentiation(base, exponent / 2); + if ((exponent % 2) == 0) + return resultOnHalfExponent * resultOnHalfExponent; + else + return resultOnHalfExponent * resultOnHalfExponent * base; + + } + + public static int fastRecursiveExponentiationModulo(int base, int exponent, int mod) { + if (exponent == 0) + return 1; + if (exponent == 1) + return base; + + final int resultOnHalfExponent = fastRecursiveExponentiationModulo(base, exponent / 2, mod); + if ((exponent % 2) == 0) + return (resultOnHalfExponent * resultOnHalfExponent) % mod; + else + return (((resultOnHalfExponent * resultOnHalfExponent) % mod) * base) % mod; + } +} diff --git a/src/com/jwetherell/algorithms/mathematics/FastFourierTransform.java b/src/com/jwetherell/algorithms/mathematics/FastFourierTransform.java new file mode 100644 index 00000000..30e2ecd8 --- /dev/null +++ b/src/com/jwetherell/algorithms/mathematics/FastFourierTransform.java @@ -0,0 +1,52 @@ +package com.jwetherell.algorithms.mathematics; + +import com.jwetherell.algorithms.numbers.Complex; + +/** + * A fast Fourier transform (FFT) algorithm computes the discrete Fourier transform (DFT) of a sequence, or its inverse. + * Fourier analysis converts a signal from its original domain (often time or space) to a representation in the frequency + * domain and vice versa. An FFT rapidly computes such transformations by factorizing the DFT matrix into a product of + * sparse (mostly zero) factors. + *

+ * @see Fast Fourier Transform (Wikipedia) + *
+ * @author Mateusz Cianciara + * @author Justin Wetherell + */ +public class FastFourierTransform { + + private FastFourierTransform() { } + + /** + * The Cooley–Tukey algorithm, named after J.W. Cooley and John Tukey, is the most common fast Fourier transform + * (FFT) algorithm. It re-expresses the discrete Fourier transform (DFT) of an arbitrary composite size N = N1N2 + * in terms of N1 smaller DFTs of sizes N2, recursively, to reduce the computation time to O(N log N) for highly + * composite N (smooth numbers). + *

+ * @see Cooley–Tukey Algorithm (Wikipedia) + *
+ * @param coefficients size must be power of 2 + */ + public static void cooleyTukeyFFT(Complex[] coefficients) { + final int size = coefficients.length; + if (size <= 1) + return; + + final Complex[] even = new Complex[size / 2]; + final Complex[] odd = new Complex[size / 2]; + for (int i = 0; i < size; i++) { + if (i % 2 == 0) { + even[i / 2] = coefficients[i]; + } else { + odd[(i - 1) / 2] = coefficients[i]; + } + } + cooleyTukeyFFT(even); + cooleyTukeyFFT(odd); + for (int k = 0; k < size / 2; k++) { + Complex t = Complex.polar(1.0, -2 * Math.PI * k / size).multiply(odd[k]); + coefficients[k] = even[k].add(t); + coefficients[k + size / 2] = even[k].sub(t); + } + } +} diff --git a/src/com/jwetherell/algorithms/mathematics/GreatestCommonDivisor.java b/src/com/jwetherell/algorithms/mathematics/GreatestCommonDivisor.java new file mode 100644 index 00000000..4ef25cd2 --- /dev/null +++ b/src/com/jwetherell/algorithms/mathematics/GreatestCommonDivisor.java @@ -0,0 +1,59 @@ +package com.jwetherell.algorithms.mathematics; + +/** + * In mathematics, the greatest common divisor (gcd) of two or more integers, when at least one of them is not + * zero, is the largest positive integer that is a divisor of both numbers. + *

+ * @see Greatest Common Divisor (Wikipedia) + *
+ * @author Szymon Stankiewicz + * @author Justin Wetherell + */ +public class GreatestCommonDivisor { + + /** + * Calculate greatest common divisor of two numbers using recursion. + *

+ * Time complexity O(log(a+b)) + *
+ * @param a Long integer + * @param b Long integer + * @return greatest common divisor of a and b + */ + public static long gcdUsingRecursion(long a, long b) { + a = Math.abs(a); + b = Math.abs(b); + return a == 0 ? b : gcdUsingRecursion(b%a, a); + } + + /** + * A much more efficient method is the Euclidean algorithm, which uses a division algorithm such as long division + * in combination with the observation that the gcd of two numbers also divides their difference. + *

+ * @see Euclidean Algorithm (Wikipedia) + */ + public static final long gcdUsingEuclides(long x, long y) { + long greater = x; + long smaller = y; + if (y > x) { + greater = y; + smaller = x; + } + + long result = 0; + while (true) { + if (smaller == greater) { + result = smaller; // smaller == greater + break; + } + + greater -= smaller; + if (smaller > greater) { + long temp = smaller; + smaller = greater; + greater = temp; + } + } + return result; + } +} diff --git a/src/com/jwetherell/algorithms/mathematics/Knapsack.java b/src/com/jwetherell/algorithms/mathematics/Knapsack.java index c981132e..09079ce9 100644 --- a/src/com/jwetherell/algorithms/mathematics/Knapsack.java +++ b/src/com/jwetherell/algorithms/mathematics/Knapsack.java @@ -3,6 +3,15 @@ import java.util.ArrayList; import java.util.List; +/** + * The knapsack problem or rucksack problem is a problem in combinatorial optimization: Given a set of items, each with a weight and a value, determine the number of each item to include in a + * collection so that the total weight is less than or equal to a given limit and the total value is as large as possible. It derives its name from the problem faced by someone who is constrained + * by a fixed-size knapsack and must fill it with the most valuable items. + *

+ * @see Knapsack Problem (Wikipedia) + *
+ * @author Justin Wetherell + */ public class Knapsack { public static final int[] zeroOneKnapsack(int[] values, int[] weights, int capacity) { @@ -28,10 +37,10 @@ public static final int[] zeroOneKnapsack(int[] values, int[] weights, int capac } } - List list = new ArrayList(); + final List list = new ArrayList(); int i = height - 1; int j = width - 1; - while (i != 0 || j != 0) { + while (i != 0 && j != 0) { int current = output[i][j]; int above = output[i - 1][j]; if (current == above) { @@ -52,5 +61,4 @@ public static final int[] zeroOneKnapsack(int[] values, int[] weights, int capac return result; } - } diff --git a/src/com/jwetherell/algorithms/mathematics/LUDecomposition.java b/src/com/jwetherell/algorithms/mathematics/LUDecomposition.java new file mode 100644 index 00000000..3ff2ec3b --- /dev/null +++ b/src/com/jwetherell/algorithms/mathematics/LUDecomposition.java @@ -0,0 +1,100 @@ +package com.jwetherell.algorithms.mathematics; + +import com.jwetherell.algorithms.data_structures.Matrix; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * LU decomposition of matrix M produces 2 matrices L and U such that M = L*U + * where L is lower triangular matrix and U is upper triangular matrix + *

+ * @see LU Decomposition (Wikipedia) + *
+ * @author Mateusz Cianciara + * @author Justin Wetherell + */ +public class LUDecomposition { + + private int n = 0; + private Double[][] L = null; + private Double[][] A = null; + private Integer[] permutation = null; + + public Matrix getL() { + return new Matrix(n, n, L); + } + + public Matrix getU() { + return new Matrix(n, n, A); + } + + public List getPermutation() { + return new ArrayList(Arrays.asList(permutation)); + } + + public LUDecomposition(Matrix input) { + if (input.getCols() != input.getRows()) + throw new IllegalArgumentException("Matrix is not square"); + + n = input.getCols(); + L = new Double[n][n]; + A = new Double[n][n]; + permutation = new Integer[n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + L[i][j] = 0.0; + A[i][j] = input.get(i, j); + } + } + for (int i = 0; i < n; i++) { + L[i][i] = 1.0; + permutation[i] = i; + } + for (int row = 0; row < n; row++) { + // find max in column + int max_in_col = row; + double curr_big = Math.abs(A[row][row]); + for (int k = row + 1; k < n; k++) { + if (curr_big < Math.abs(A[k][row])) { + max_in_col = k; + curr_big = Math.abs(A[k][row]); + } + } + + //swap rows + if (row != max_in_col) { + for (int i = 0; i < n; i++) { + double temp = A[row][i]; + A[row][i] = A[max_in_col][i]; + A[max_in_col][i] = temp; + + if (i < row) { + temp = L[row][i]; + L[row][i] = L[max_in_col][i]; + L[max_in_col][i] = temp; + } + } + final int temp = permutation[row]; + permutation[row] = permutation[max_in_col]; + permutation[max_in_col] = temp; + } + + //zero column number row + final double p = A[row][row]; + if (p == 0) + return; + + for (int i = row + 1; i < n; i++) { + final double y = A[i][row]; + L[i][row] = y / p; + + for (int j = row; j < n; j++) { + A[i][j] -= A[row][j] * (y / p); + } + } + } + } +} diff --git a/src/com/jwetherell/algorithms/mathematics/Modular.java b/src/com/jwetherell/algorithms/mathematics/Modular.java new file mode 100644 index 00000000..1c78de2c --- /dev/null +++ b/src/com/jwetherell/algorithms/mathematics/Modular.java @@ -0,0 +1,143 @@ +package com.jwetherell.algorithms.mathematics; + +/** + * In mathematics, modular arithmetic is a system of arithmetic for integers, where numbers "wrap around" + * upon reaching a certain value—the modulus (plural moduli). The modern approach to modular arithmetic was + * developed by Carl Friedrich Gauss in his book Disquisitiones Arithmeticae, published in 1801. + *

+ * @see Modular Arithmetic (Wikipedia) + *
+ * @author Szymon Stankiewicz + * @author Justin Wetherell + */ +public class Modular { + + private static long modularAbs(long n, long mod) { + n %= mod; + if (n < 0) + n += mod; + return n; + } + + + /** + * Adds two numbers in modulo arithmetic. + * This function is safe for large numbers and won't overflow long. + * + * @param a + * @param b + * @param mod grater than 0 + * @return (a+b)%mod + */ + public static long add(long a, long b, long mod) { + if(mod <= 0) + throw new IllegalArgumentException("Mod argument is not grater then 0"); + a = modularAbs(a, mod); + b = modularAbs(b, mod); + if(b > mod-a) { + return b - (mod - a); + } + return (a + b)%mod; + } + + /** + * Subtract two numbers in modulo arithmetic. + * This function is safe for large numbers and won't overflow or underflow long. + * + * @param a + * @param b + * @param mod grater than 0 + * @return (a-b)%mod + */ + public static long subtract(long a, long b, long mod) { + if(mod <= 0) + throw new IllegalArgumentException("Mod argument is not grater then 0"); + return add(a, -b, mod); + } + + /** + * Multiply two numbers in modulo arithmetic. + * This function is safe for large numbers and won't overflow or underflow long. + * + * Complexity O(log b) + * + * @param a + * @param b + * @param mod grater than 0 + * @return (a*b)%mod + */ + + public static long multiply(long a, long b, long mod) { + if(mod <= 0) + throw new IllegalArgumentException("Mod argument is not grater then 0"); + a = modularAbs(a, mod); + b = modularAbs(b, mod); + if(b == 0) return 0; + return add(multiply(add(a, a, mod), b/2, mod), (b%2 == 1 ? a : 0), mod); + } + + /** + * Calculate power in modulo arithmetic. + * This function is safe for large numbers and won't overflow or underflow long. + * + * Complexity O(log a * log b) + * + * @param a + * @param b integer grater or equal to zero + * @param mod grater than 0 + * @return (a^b)%mod + */ + public static long pow(long a, long b, long mod) { + if(mod <= 0) + throw new IllegalArgumentException("Mod argument is not grater then 0"); + if (b < 0) + throw new IllegalArgumentException("Exponent have to be grater or equal to zero"); + a = modularAbs(a, mod); + if (a == 0 && b == 0) + throw new IllegalArgumentException("0^0 expression"); + if (a == 0) + return 0; + long res = 1; + while(b > 0) { + if(b%2 == 1) res = multiply(res, a, mod); + a = multiply(a, a, mod); + b /= 2; + } + return res; + } + + /** + * Divide two numbers in modulo arithmetic. + * This function is safe for large numbers and won't overflow or underflow long. + * b and mod have to be coprime. + * + * Complexity O(sqrt(mod)) + * + * @param a + * @param b non zero + * @param mod grater than 0 + * @return (a/b)%mod + */ + + public static long divide(long a, long b, long mod) { + a = modularAbs(a, mod); + b = modularAbs(b, mod); + if(mod <= 0) + throw new IllegalArgumentException("Mod argument is not grater then 0"); + if (b == 0) + throw new IllegalArgumentException("Dividing by zero"); + if (GreatestCommonDivisor.gcdUsingRecursion(b, mod) != 1) { + throw new IllegalArgumentException("b and mod are not coprime"); + } + if (a == 0) { + return 0; + } + if (b == 1) { + return a; + } + + long reverted = pow(b, Coprimes.getNumberOfCoprimes(mod)-1, mod); + return multiply(reverted, a, mod); + + } +} \ No newline at end of file diff --git a/src/com/jwetherell/algorithms/mathematics/Multiplication.java b/src/com/jwetherell/algorithms/mathematics/Multiplication.java index e2d0608d..98db58a7 100644 --- a/src/com/jwetherell/algorithms/mathematics/Multiplication.java +++ b/src/com/jwetherell/algorithms/mathematics/Multiplication.java @@ -1,5 +1,10 @@ package com.jwetherell.algorithms.mathematics; +import java.util.ArrayList; +import java.util.Collections; + +import com.jwetherell.algorithms.numbers.Complex; + public class Multiplication { public static final long multiplication(int a, int b) { @@ -47,4 +52,226 @@ public static final long multiplyUsingLogs(int a, int b) { long result = Math.round(Math.pow(10, (Math.log10(absA) + Math.log10(absB)))); return (a > 0 && b > 0 || a < 0 && b < 0) ? result : -result; } + + public static String multiplyUsingFFT(String a, String b) { + if (a.equals("0") || b.equals("0")) { + return "0"; + } + boolean negative = false; + if ((a.charAt(0) == '-' && b.charAt(0) != '-') || (a.charAt(0) != '-' && b.charAt(0) == '-')) { + negative = true; + } + if (a.charAt(0) == '-') { + a = a.substring(1); + } + if (b.charAt(0) == '-') { + b = b.substring(1); + } + int size = 1; + while (size < (a.length() + b.length())) { + size *= 2; + } + Complex[] aCoefficients = new Complex[size]; + Complex[] bCoefficients = new Complex[size]; + for (int i = 0; i < size; i++) { + aCoefficients[i] = new Complex(); + bCoefficients[i] = new Complex(); + } + for (int i = 0; i < a.length(); i++) { + aCoefficients[i] = new Complex((double) (Character.getNumericValue(a.charAt(a.length() - i - 1))), 0.0); + } + for (int i = 0; i < b.length(); i++) { + bCoefficients[i] = new Complex((double) (Character.getNumericValue(b.charAt(b.length() - i - 1))), 0.0); + } + + FastFourierTransform.cooleyTukeyFFT(aCoefficients); + FastFourierTransform.cooleyTukeyFFT(bCoefficients); + + for (int i = 0; i < size; i++) { + aCoefficients[i] = aCoefficients[i].multiply(bCoefficients[i]); + } + for (int i = 0; i < size / 2; i++) { + Complex temp = aCoefficients[i]; + aCoefficients[i] = aCoefficients[size - i - 1]; + aCoefficients[size - i - 1] = temp; + } + FastFourierTransform.cooleyTukeyFFT(aCoefficients); + + ArrayList res = new ArrayList(); + int pass = 0; + for (int i = 0; i < size; i++) { + res.add((int) (pass + Math.floor((aCoefficients[i].abs() + 1) / size))); + if (res.get(i) >= 10) { + pass = res.get(i) / 10; + res.set(i, res.get(i) % 10); + } else { + pass = 0; + } + } + Collections.reverse(res); + StringBuilder result = new StringBuilder(); + if (negative) { + result.append('-'); + } + boolean startPrinting = false; + for (Integer x : res) { + if (x != 0) { + startPrinting = true; + } + if (startPrinting) { + result.append(x); + } + } + return result.toString(); + } + + public static String multiplyUsingLoopWithStringInput(String a, String b) { + int k,i,j,carry=0,rem,flag=0,lim1,lim2,mul; + + boolean aIsNegative = false; + ArrayList first = new ArrayList(); + for (char n : a.toCharArray()){ + if (n=='-') { + aIsNegative = true; + continue; + } + first.add(n-'0'); + } + + boolean bIsNegative = false; + ArrayList second = new ArrayList(); + for (char n : b.toCharArray()){ + if (n=='-') { + bIsNegative = true; + continue; + } + second.add(n-'0'); + } + + lim1=first.size()-1; + lim2=second.size()-1; + + ArrayList res = new ArrayList(Collections.nCopies(first.size()+second.size(), 0)); + for (i=0;i<=lim1;i++) { + k=i; + for (j=0;j<=lim2;j++) { + int f = first.get(i); + int s = second.get(j); + mul=f*s; + res.set(k,res.get(k)+(mul/10)); + k++; + res.set(k,res.get(k)+(mul%10)); + } + } + + for (i=(lim1+lim2)+1;i>=0;i--) { + if (flag==1){ + res.set(i,res.get(i)+carry); + flag=0; + } + + if (res.get(i)>=10 && i!=0) { + rem=res.get(i)%10; + carry=res.get(i)/10; + res.set(i,rem); + flag++; + } + } + + StringBuilder sb = new StringBuilder(); + if (aIsNegative ^ bIsNegative) + sb.append('-'); + boolean zeroCheck = true; + for (Integer s : res) { + if (zeroCheck && s.equals(0)) + continue; + zeroCheck = false; + sb.append(s); + } + return sb.toString(); + } + + public static int multiplyUsingLoopWithIntegerInput(int a, int b) { + boolean aIsNegative = a<0; + boolean bIsNegative = b<0; + a = Math.abs(a); + b = Math.abs(b); + + // Find the largest multiple of ten which is larger than 'a' + int largerMultipleA=1; + int numberOfDigitsInA=0; + while (largerMultipleA=0; i--) { + int k=numberOfDigitsInA-i; + // reset + largerMultipleB = originalMultipleB; + b = originalB; + for (int j=numberOfDigitsInB; j>=0; j--) { + int f = a/largerMultipleA; + int s = b/largerMultipleB; + + b %= largerMultipleB; + largerMultipleB /= 10; + + mul=f*s; + res[k] = res[k]+(mul/10); + k++; + res[k] = res[k]+(mul%10); + } + a %= largerMultipleA; + largerMultipleA /= 10; + } + + for (int i=(numberOfDigitsInA+numberOfDigitsInB)+1; i>=0; i--) { + if (flag==1){ + res[i] = res[i]+carry; + flag=0; + } + + if (res[i] >=10 && i!=0) { + rem = res[i]%10; + carry = res[i] /10; + res[i] = rem; + flag++; + } + } + + int result = 0; + int m = 1; + for (int idx=res.length-1; idx>=0; idx--) { + int s = res[idx]; + result += s*m; + m *= 10; + } + // adjust for negatives + if (aIsNegative ^ bIsNegative) + result *= -1; + return result; + } } diff --git a/src/com/jwetherell/algorithms/mathematics/Permutations.java b/src/com/jwetherell/algorithms/mathematics/Permutations.java new file mode 100644 index 00000000..144a17c4 --- /dev/null +++ b/src/com/jwetherell/algorithms/mathematics/Permutations.java @@ -0,0 +1,108 @@ +package com.jwetherell.algorithms.mathematics; + +import java.util.LinkedList; +import java.util.List; + +/** + * In mathematics, the notion of permutation relates to the act of arranging all the members of a set into some sequence + * or order, or if the set is already ordered, rearranging (reordering) its elements, a process called permuting. + *

+ * @see Permutation (Wikipedia) + *
+ * @author Justin Wetherell + * @author Lucjan Roslanowski + */ +public class Permutations { + + private Permutations() { } + + /** + * N! permutation of the characters in the string (in order) + */ + public static String[] permutations(String stringToGeneratePermutationsFrom) { + final int size = numberOfPermutations(stringToGeneratePermutationsFrom.length()); + final String[] list = new String[size]; + final char[] prefix = new char[0]; + final char[] chars = stringToGeneratePermutationsFrom.toCharArray(); + permutations(list, 0, prefix, chars, 0, chars.length); + return list; + } + + private static final int numberOfPermutations(int N) { + // factorial + int result = N; + while (N > 1) + result *= --N; + return result; + } + + private static final int permutations(String[] list, int index, char[] prefix, char[] remaining, int prefixLength, int remainingLength) { + final int N = remainingLength-prefixLength; + if (N == 0) { + list[index]=new String(prefix); + index++; + } else { + for (int i=0; i + * int numbers[] = {7,5,3}; + * LinkedList> result = getAllPermutations(numbers); + */ + public static final List> getAllPermutations(final N[] numbers){ + final List> result = new LinkedList>(); + return getAllPermutations(numbers, result); + } + + private static final List> getAllPermutations(final N[] numbers, List> result){ + //numbers given in an array are also a permutation + LinkedList firstPermutation = new LinkedList(); + for (N el : numbers) + firstPermutation.add(el); + result.add(firstPermutation); + //let's permute all elements in array starting from index 0 + return permute(numbers, 0, result); + } + + private static final List> permute(final N[] numbers, int currentElementIndex, List> result){ + if(currentElementIndex == numbers.length - 1) + return result; + + for(int i = currentElementIndex; i < numbers.length; ++i){ + //swapping two elements + N temp = numbers[i]; + numbers[i] = numbers[currentElementIndex]; + numbers[currentElementIndex] = temp; + + permute(numbers, currentElementIndex + 1,result); + + //all next permutation found + if(i != currentElementIndex){ + LinkedList nextPermutation = new LinkedList(); + for(int j = 0; j < numbers.length; j++) + nextPermutation.add(numbers[j]); + result.add(nextPermutation); + } + + //swapping back two elements + temp = numbers[i]; + numbers[i] = numbers[currentElementIndex]; + numbers[currentElementIndex] = temp; + } + return result; + } +} diff --git a/src/com/jwetherell/algorithms/mathematics/Primes.java b/src/com/jwetherell/algorithms/mathematics/Primes.java index b633f0d3..ee4ca62b 100644 --- a/src/com/jwetherell/algorithms/mathematics/Primes.java +++ b/src/com/jwetherell/algorithms/mathematics/Primes.java @@ -1,12 +1,28 @@ package com.jwetherell.algorithms.mathematics; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; +/** + * @author Justin Wetherell + * @author Bartlomiej Drozd + */ public class Primes { - public static final Map getPrimeFactorization(long n) { + /** + * In number theory, the prime factors of a positive integer are the prime numbers that divide that integer exactly. The prime + * factorization of a positive integer is a list of the integer's prime factors, together with their multiplicities; the process + * of determining these factors is called integer factorization. The fundamental theorem of arithmetic says that every positive + * integer has a single unique prime factorization. + *

+ * @see Prime Factor (Wikipedia) + *
+ */ + public static final Map getPrimeFactorization(long number) { Map map = new HashMap(); + long n = number; int c = 0; // for each potential factor i for (long i = 2; i * i <= n; i++) { @@ -32,34 +48,34 @@ public static final Map getPrimeFactorization(long n) { return map; } - /* + /** * isPrime() using the square root properties - * + *

* 1 is not a prime. All primes except 2 are odd. All primes greater than 3 * can be written in the form 6k+/-1. Any number n can have only one - * primefactor greater than n . The consequence for primality testing of a + * prime factor greater than n . The consequence for primality testing of a * number n is: if we cannot find a number f less than or equal n that - * divides n then n is prime: the only primefactor of n is n itself - */ - public static final boolean isPrime(long value) { - if (value == 1) + * divides n then n is prime: the only prime factor of n is n itself + **/ + public static final boolean isPrime(long number) { + if (number == 1) return false; - if (value < 4) + if (number < 4) return true; // 2 and 3 are prime - if (value % 2 == 0) + if (number % 2 == 0) return false; // short circuit - if (value < 9) + if (number < 9) return true; // we have already excluded 4, 6 and 8. - // (testing for 5 & 7) - if (value % 3 == 0) + // (testing for 5 & 7) + if (number % 3 == 0) return false; // short circuit - long r = (long) (Math.sqrt(value)); // n rounded to the greatest integer - // r so that r*r<=n + long r = (long) (Math.sqrt(number)); // n rounded to the greatest integer + // r so that r*r<=n int f = 5; while (f <= r) { - if (value % f == 0) + if (number % f == 0) return false; - if (value % (f + 2) == 0) + if (number % (f + 2) == 0) return false; f += 6; } @@ -67,20 +83,36 @@ public static final boolean isPrime(long value) { } /* - * Sieve of Eratosthenes + * Sieve of Eratosthenes, only has to be set once. */ private static boolean[] sieve = null; + /** + * In mathematics, the sieve of Eratosthenes is a simple, ancient algorithm for finding all prime numbers up to any given limit. + *

+ * It does so by iteratively marking as composite (i.e., not prime) the multiples of each prime, starting with the first prime + * number, 2. The multiples of a given prime are generated as a sequence of numbers starting from that prime, with constant + * difference between them that is equal to that prime. + *

+ * @see Sieve of Eratosthenes (Wikipedia) + *
+ */ public static final boolean sieveOfEratosthenes(int number) { + if (number == 1) { + return false; + } if (sieve == null || number >= sieve.length) { int start = 2; - if (sieve == null) - sieve = new boolean[number]; - else - start = sieve.length; - for (int i = start; Math.pow(i, 2) < sieve.length; i++) { + + if (sieve == null) { + sieve = new boolean[number + 1]; + } else if (number >= sieve.length) { + sieve = Arrays.copyOf(sieve, number + 1); + } + + for (int i = start; i <= Math.sqrt(number); i++) { if (!sieve[i]) { - for (int j = (int) Math.pow(i, 2); j < sieve.length; j += i) { + for (int j = i * 2; j <= number; j += i) { sieve[j] = true; } } @@ -88,4 +120,50 @@ public static final boolean sieveOfEratosthenes(int number) { } return !sieve[number]; } + + /** + * Miller-Rabin primality test is the fastest way to check if number is prime. + * Regular version of this algorithm returns false when number is composite and true + * when number is probably prime. Here is implemented a deterministic version of this + * algorithm, witnesses are not randomized. Used set of witnesses guarantees that result + * will be correct for sure (not probably) for any number lower than 10^18. + *

+ * @see Miller-Rabin Primality Test (Wikipedia) + *
+ */ + public static final boolean millerRabinTest(int number) { + final List witnesses = Arrays.asList(2, 325, 9375, 28178, 450775, 9780504, 1795265022); + + if (number == 0 || number == 1) + return false; + if (number == 2 || number == 3) + return true; + + int maximumPowerOf2 = 0; + while (((number - 1) % Exponentiation.fastRecursiveExponentiation(2, maximumPowerOf2)) == 0) + maximumPowerOf2++; + maximumPowerOf2--; + + int d = (number - 1) / Exponentiation.fastRecursiveExponentiation(2, maximumPowerOf2); + boolean isPrime = true; + for (int a : witnesses) { + if (a > number) + break; + if (Exponentiation.fastRecursiveExponentiationModulo(a, d, number) != 1) { + boolean isLocalPrime = false; + for (int r = 0; r < maximumPowerOf2; r++) { + if (Exponentiation.fastRecursiveExponentiationModulo(a, d * Exponentiation.fastRecursiveExponentiation(2, r), number) == (number - 1)) { + isLocalPrime = true; + break; + } + } + if (!isLocalPrime) { + isPrime = false; + break; + } + } + } + + return isPrime; + } } diff --git a/src/com/jwetherell/algorithms/mathematics/RamerDouglasPeucker.java b/src/com/jwetherell/algorithms/mathematics/RamerDouglasPeucker.java new file mode 100644 index 00000000..e43d24c9 --- /dev/null +++ b/src/com/jwetherell/algorithms/mathematics/RamerDouglasPeucker.java @@ -0,0 +1,92 @@ +package com.jwetherell.algorithms.mathematics; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Ramer–Douglas–Peucker algorithm (RDP) is an algorithm for reducing the number of points in a + * curve that is approximated by a series of points. + *

+ * @see Ramer–Douglas–Peucker Algorithm (Wikipedia) + *
+ * @author Justin Wetherell + */ +public class RamerDouglasPeucker { + + private RamerDouglasPeucker() { } + + private static final double sqr(double x) { + return Math.pow(x, 2); + } + + private static final double distanceBetweenPoints(double vx, double vy, double wx, double wy) { + return sqr(vx - wx) + sqr(vy - wy); + } + + private static final double distanceToSegmentSquared(double px, double py, double vx, double vy, double wx, double wy) { + final double l2 = distanceBetweenPoints(vx, vy, wx, wy); + if (l2 == 0) + return distanceBetweenPoints(px, py, vx, vy); + final double t = ((px - vx) * (wx - vx) + (py - vy) * (wy - vy)) / l2; + if (t < 0) + return distanceBetweenPoints(px, py, vx, vy); + if (t > 1) + return distanceBetweenPoints(px, py, wx, wy); + return distanceBetweenPoints(px, py, (vx + t * (wx - vx)), (vy + t * (wy - vy))); + } + + private static final double perpendicularDistance(double px, double py, double vx, double vy, double wx, double wy) { + return Math.sqrt(distanceToSegmentSquared(px, py, vx, vy, wx, wy)); + } + + private static final void douglasPeucker(List list, int s, int e, double epsilon, List resultList) { + // Find the point with the maximum distance + double dmax = 0; + int index = 0; + + final int start = s; + final int end = e-1; + for (int i=start+1; i dmax) { + index = i; + dmax = d; + } + } + // If max distance is greater than epsilon, recursively simplify + if (dmax > epsilon) { + // Recursive call + douglasPeucker(list, s, index, epsilon, resultList); + douglasPeucker(list, index, e, epsilon, resultList); + } else { + if ((end-start)>0) { + resultList.add(list.get(start)); + resultList.add(list.get(end)); + } else { + resultList.add(list.get(start)); + } + } + } + + /** + * Given a curve composed of line segments find a similar curve with fewer points. + * + * @param list List of Double[] points (x,y) + * @param epsilon Distance dimension + * @return Similar curve with fewer points + */ + public static final List douglasPeucker(List list, double epsilon) { + final List resultList = new ArrayList(); + douglasPeucker(list, 0, list.size(), epsilon, resultList); + return resultList; + } +} diff --git a/src/com/jwetherell/algorithms/numbers/Complex.java b/src/com/jwetherell/algorithms/numbers/Complex.java new file mode 100644 index 00000000..bef753d4 --- /dev/null +++ b/src/com/jwetherell/algorithms/numbers/Complex.java @@ -0,0 +1,60 @@ +package com.jwetherell.algorithms.numbers; + +/** + * A complex number is a number that can be expressed in the form a + bi, where a and b are real numbers and i is the + * imaginary unit, satisfying the equation i2 = −1.[1] In this expression, a is the real part and b is the imaginary + * part of the complex number. If z=a+bi z=a+bi, then Rz=a, Iz=b. + *

+ * @see Complex Number (Wikipedia) + *
+ * @author Mateusz Cianciara + * @author Justin Wetherell + */ +public class Complex { + + public double real; + public double imaginary; + + public Complex() { + this.real = 0.0; + this.imaginary = 0.0; + } + + public Complex(double r, double i) { + this.real = r; + this.imaginary = i; + } + + public Complex multiply(final Complex x) { + final Complex copy = new Complex(this.real, this.imaginary); + copy.real = this.real * x.real - this.imaginary * x.imaginary; + copy.imaginary = this.imaginary * x.real + this.real * x.imaginary; + return copy; + } + + public Complex add(final Complex x) { + final Complex copy = new Complex(this.real, this.imaginary); + copy.real += x.real; + copy.imaginary += x.imaginary; + return copy; + } + + public Complex sub(final Complex x) { + final Complex copy = new Complex(this.real, this.imaginary); + copy.real -= x.real; + copy.imaginary -= x.imaginary; + return copy; + } + + public double abs() { + return Math.sqrt(this.real * this.real + this.imaginary * this.imaginary); + } + + public String toString() { + return "(" + this.real + "," + this.imaginary + ")"; + } + + public static Complex polar(final double rho, final double theta) { + return (new Complex(rho * Math.cos(theta), rho * Math.sin(theta))); + } +} diff --git a/src/com/jwetherell/algorithms/numbers/Integers.java b/src/com/jwetherell/algorithms/numbers/Integers.java index d42785a0..fb86fd3d 100644 --- a/src/com/jwetherell/algorithms/numbers/Integers.java +++ b/src/com/jwetherell/algorithms/numbers/Integers.java @@ -9,7 +9,9 @@ public class Integers { private static final BigDecimal ZERO = new BigDecimal(0); private static final BigDecimal TWO = new BigDecimal(2); - public static final String toBinaryUsingDivideAndModulus(int integer) { + public static final String toBinaryUsingDivideAndModulus(int numberToConvert) { + int integer = numberToConvert; + if (integer<0) throw new IllegalArgumentException("Method argument cannot be negative. number="+integer); StringBuilder builder = new StringBuilder(); int temp = 0; while (integer > 0) { @@ -20,7 +22,9 @@ public static final String toBinaryUsingDivideAndModulus(int integer) { return builder.reverse().toString(); } - public static final String toBinaryUsingShiftsAndModulus(int integer) { + public static final String toBinaryUsingShiftsAndModulus(int numberToConvert) { + int integer = numberToConvert; + if (integer<0) throw new IllegalArgumentException("Method argument cannot be negative. number="+integer); StringBuilder builder = new StringBuilder(); int temp = 0; while (integer > 0) { @@ -31,7 +35,9 @@ public static final String toBinaryUsingShiftsAndModulus(int integer) { return builder.reverse().toString(); } - public static final String toBinaryUsingBigDecimal(int integer) { + public static final String toBinaryUsingBigDecimal(int numberToConvert) { + int integer = numberToConvert; + if (integer<0) throw new IllegalArgumentException("Method argument cannot be negative. number="+integer); StringBuilder builder = new StringBuilder(); BigDecimal number = new BigDecimal(integer); BigDecimal[] decimals = null; @@ -43,7 +49,9 @@ public static final String toBinaryUsingBigDecimal(int integer) { return builder.reverse().toString(); } - public static final String toBinaryUsingDivideAndDouble(int integer) { + public static final String toBinaryUsingDivideAndDouble(int numberToConvert) { + int integer = numberToConvert; + if (integer<0) throw new IllegalArgumentException("Method argument cannot be negative. number="+integer); StringBuilder builder = new StringBuilder(); double temp = 0d; while (integer > 0) { @@ -54,32 +62,8 @@ public static final String toBinaryUsingDivideAndDouble(int integer) { return builder.reverse().toString(); } - public static final int euclidsGreatestCommonDivsor(int x, int y) { - int greater = x; - int smaller = y; - if (y > x) { - greater = y; - smaller = x; - } - - int result = 0; - while (true) { - if (smaller == greater) { - result = smaller; // smaller == greater - break; - } - - greater -= smaller; - if (smaller > greater) { - int temp = smaller; - smaller = greater; - greater = temp; - } - } - return result; - } - - public static final boolean powerOfTwoUsingLoop(int number) { + public static final boolean powerOfTwoUsingLoop(int numberToCheck) { + int number = numberToCheck; if (number == 0) return false; while (number % 2 == 0) { @@ -90,7 +74,8 @@ public static final boolean powerOfTwoUsingLoop(int number) { return true; } - public static final boolean powerOfTwoUsingRecursion(int number) { + public static final boolean powerOfTwoUsingRecursion(int numberToCheck) { + int number = numberToCheck; if (number == 1) return true; if (number == 0 || number % 2 != 0) @@ -98,15 +83,17 @@ public static final boolean powerOfTwoUsingRecursion(int number) { return powerOfTwoUsingRecursion(number / 2); } - public static final boolean powerOfTwoUsingLog(int number) { + public static final boolean powerOfTwoUsingLog(int numberToCheck) { + int number = numberToCheck; double doubleLog = Math.log10(number) / Math.log10(2); int intLog = (int) doubleLog; - if (doubleLog == intLog) + if (Double.compare(doubleLog, intLog) == 0) return true; return false; } - public static final boolean powerOfTwoUsingBits(int number) { + public static final boolean powerOfTwoUsingBits(int numberToCheck) { + int number = numberToCheck; if (number != 0 && ((number & (number - 1)) == 0)) return true; return false; @@ -132,7 +119,7 @@ public static final boolean powerOfTwoUsingBits(int number) { singleDigits.put(14,"fourteen"); singleDigits.put(15,"fifteen"); singleDigits.put(16,"sixteen"); - singleDigits.put(17,"seventee"); + singleDigits.put(17,"seventeen"); singleDigits.put(18,"eighteen"); singleDigits.put(19,"nineteen"); } @@ -142,12 +129,12 @@ public static final boolean powerOfTwoUsingBits(int number) { multiDigits.put(10,"ten"); multiDigits.put(20,"twenty"); multiDigits.put(30,"thirty"); - multiDigits.put(40,"fourty"); + multiDigits.put(40,"forty"); multiDigits.put(50,"fifty"); multiDigits.put(60,"sixty"); multiDigits.put(70,"seventy"); multiDigits.put(80,"eighty"); - multiDigits.put(90,"ninty"); + multiDigits.put(90,"ninety"); } private static final int BILLION = 1000000000; @@ -156,8 +143,9 @@ public static final boolean powerOfTwoUsingBits(int number) { private static final int HUNDRED = 100; private static final int TEN = 10; - private static final String handleUnderOneThousand(int x) { + private static final String handleUnderOneThousand(int number) { StringBuilder builder = new StringBuilder(); + int x = number; int m = x / HUNDRED; int r = x % HUNDRED; if (m > 0) { @@ -183,7 +171,9 @@ private static final String handleUnderOneThousand(int x) { return builder.toString(); } - public static final String toEnglish(int x) { + public static final String toEnglish(int number) { + int x = number; + if (x>Integer.MAX_VALUE || x<=Integer.MIN_VALUE) throw new IllegalArgumentException("Number has to be <= Integer.MAX_VALUE and > Integer.MIN_VALUE. number="+x); StringBuilder builder = new StringBuilder(); if (x==0) { //Zero is a special case @@ -195,6 +185,7 @@ public static final String toEnglish(int x) { boolean thousand = false; if (x<0) { builder.append("negative "); + // Make the number positive x = x * -1; } int m = x / BILLION; diff --git a/src/com/jwetherell/algorithms/numbers/Longs.java b/src/com/jwetherell/algorithms/numbers/Longs.java index dc642a85..e3f1f3db 100644 --- a/src/com/jwetherell/algorithms/numbers/Longs.java +++ b/src/com/jwetherell/algorithms/numbers/Longs.java @@ -4,7 +4,9 @@ public class Longs { - public static final String toBinaryUsingDivideAndModulus(long longNumber) { + public static final String toBinaryUsingDivideAndModulus(long numberToConvert) { + long longNumber = numberToConvert; + if (longNumber<0) throw new IllegalArgumentException("Method argument cannot be negative. number="+longNumber); StringBuilder builder = new StringBuilder(); long temp = 0l; while (longNumber > 0) { @@ -15,7 +17,9 @@ public static final String toBinaryUsingDivideAndModulus(long longNumber) { return builder.reverse().toString(); } - public static final String toBinaryUsingShiftsAndModulus(long longNumber) { + public static final String toBinaryUsingShiftsAndModulus(long numberToConvert) { + long longNumber = numberToConvert; + if (longNumber<0) throw new IllegalArgumentException("Method argument cannot be negative. number="+longNumber); StringBuilder builder = new StringBuilder(); long temp = 0l; while (longNumber > 0) { @@ -26,7 +30,9 @@ public static final String toBinaryUsingShiftsAndModulus(long longNumber) { return builder.reverse().toString(); } - public static final String toBinaryUsingBigDecimal(long longNumber) { + public static final String toBinaryUsingBigDecimal(long numberToConvert) { + long longNumber = numberToConvert; + if (longNumber<0) throw new IllegalArgumentException("Method argument cannot be negative. number="+longNumber); StringBuilder builder = new StringBuilder(); BigDecimal zero = new BigDecimal(0); BigDecimal two = new BigDecimal(2); diff --git a/src/com/jwetherell/algorithms/search/BinarySearch.java b/src/com/jwetherell/algorithms/search/BinarySearch.java index 5c731430..3bcd557b 100644 --- a/src/com/jwetherell/algorithms/search/BinarySearch.java +++ b/src/com/jwetherell/algorithms/search/BinarySearch.java @@ -1,5 +1,19 @@ package com.jwetherell.algorithms.search; +/** + * In computer science, binary search, also known as half-interval search or logarithmic search, is a search algorithm that finds the position of a target value within a sorted array. Binary search + * compares the target value to the middle element of the array; if they are unequal, the half in which the target cannot lie is eliminated and the search continues on the remaining half until it is + * successful or the remaining half is empty. + *

+ * Worst-case performance O(log n)
+ * Best-case performance O(1)
+ * Average performance O(log n)
+ * Worst-case space complexity O(1)
+ *

+ * @see Binary Search (Wikipedia) + *
+ * @author Justin Wetherell + */ public class BinarySearch { private static final int SWITCH_TO_BRUTE_FORCE = 200; @@ -15,7 +29,8 @@ public static final int find(int value, int[] array, boolean optimize) { BinarySearch.sorted = null; } } - + //Recursively find the element + //@return find the element value by recursively private static int recursiveFind(int value, int start, int end, boolean optimize) { if (start == end) { int lastValue = sorted[start]; // start==end @@ -24,13 +39,15 @@ private static int recursiveFind(int value, int start, int end, boolean optimize return Integer.MAX_VALUE; } - int low = start; - int high = end + 1; // zero indexed, so add one. - int middle = low + ((high - low) / 2); + final int low = start; + final int high = end + 1; // zero indexed, so add one. + final int middle = low + ((high - low) / 2); - int middleValue = sorted[middle]; + final int middleValue = sorted[middle]; + //checks if the middle index is element if (value == middleValue) return middle; + //if value is greater than move to right if (value > middleValue) { if (optimize && (end - middle) <= SWITCH_TO_BRUTE_FORCE) return linearSearch(value, middle + 1, end); @@ -40,8 +57,12 @@ private static int recursiveFind(int value, int start, int end, boolean optimize return linearSearch(value, start, middle - 1); return recursiveFind(value, start, middle - 1, optimize); } - + //Linear search to find the element. + //@value the element we want to find. + //@start first index of the array in the array + //@end last index of the array in the array. private static final int linearSearch(int value, int start, int end) { + // From index i = start to i = end check if value matches sorted[i] for (int i = start; i <= end; i++) { int iValue = sorted[i]; if (value == iValue) diff --git a/src/com/jwetherell/algorithms/search/InterpolationSearch.java b/src/com/jwetherell/algorithms/search/InterpolationSearch.java index 5f34a842..d9050256 100644 --- a/src/com/jwetherell/algorithms/search/InterpolationSearch.java +++ b/src/com/jwetherell/algorithms/search/InterpolationSearch.java @@ -1,5 +1,16 @@ package com.jwetherell.algorithms.search; +/** + * Interpolation search is an algorithm for searching for a given key in an indexed array that has been ordered by numerical values assigned to the keys (key values). It parallels how humans search + * through a telephone book for a particular name, the key value by which the book's entries are ordered. + *

+ * Worst-case performance O(n)
+ * Average performance O(log(log(n)))
+ *

+ * @see Interpolation Search (Wikipedia) + *
+ * @author Justin Wetherell + */ public class InterpolationSearch { private static int[] sorted = null; @@ -22,7 +33,7 @@ private static int recursiveFind(int value, int start, int end) { return Integer.MAX_VALUE; } - int mid = start + ((value - sorted[start]) * (end - start)) / (sorted[end] - sorted[start]); + final int mid = start + ((value - sorted[start]) * (end - start)) / (sorted[end] - sorted[start]); if (mid < 0 || mid > end) return Integer.MAX_VALUE; int midValue = sorted[mid]; diff --git a/src/com/jwetherell/algorithms/search/LinearSearch.java b/src/com/jwetherell/algorithms/search/LinearSearch.java index ae186d26..196591e7 100644 --- a/src/com/jwetherell/algorithms/search/LinearSearch.java +++ b/src/com/jwetherell/algorithms/search/LinearSearch.java @@ -1,5 +1,18 @@ package com.jwetherell.algorithms.search; +/** + * In computer science, linear search or sequential search is a method for finding a target value within a list. It sequentially checks each element of the list for the target value until a match is + * found or until all the elements have been searched. + *

+ * Worst-case performance O(n)
+ * Best-case performance O(1)
+ * Average performance O(n)
+ * Worst-case space complexity O(1)
+ *

+ * @see Linear Search (Wikipedia) + *
+ * @author Justin Wetherell + */ public class LinearSearch { public static final int find(int value, int[] array) { diff --git a/src/com/jwetherell/algorithms/search/LowerBound.java b/src/com/jwetherell/algorithms/search/LowerBound.java new file mode 100644 index 00000000..9e6cd353 --- /dev/null +++ b/src/com/jwetherell/algorithms/search/LowerBound.java @@ -0,0 +1,35 @@ +package com.jwetherell.algorithms.search; + +/** + * Lower bound search algorithm.
+ * Lower bound is kind of binary search algorithm but:
+ * -If searched element doesn't exist function returns index of first element which is bigger than searched value.
+ * -If searched element is bigger than any array element function returns first index after last element.
+ * -If searched element is lower than any array element function returns index of first element.
+ * -If there are many values equals searched value function returns first occurrence.
+ * Behaviour for unsorted arrays is unspecified. + *

+ * Complexity O(log n). + *

+ * @author Bartlomiej Drozd + * @author Justin Wetherell + */ +public class LowerBound { + + private LowerBound() { } + + public static int lowerBound(int[] array, int length, int value) { + int low = 0; + int high = length; + while (low < high) { + final int mid = (low + high) / 2; + //checks if the value is less than middle element of the array + if (value <= array[mid]) { + high = mid; + } else { + low = mid + 1; + } + } + return low; + } +} diff --git a/src/com/jwetherell/algorithms/search/QuickSelect.java b/src/com/jwetherell/algorithms/search/QuickSelect.java index 67a7336e..3fe048a9 100644 --- a/src/com/jwetherell/algorithms/search/QuickSelect.java +++ b/src/com/jwetherell/algorithms/search/QuickSelect.java @@ -2,6 +2,17 @@ import java.util.Random; +/** + * In computer science, quickselect is a selection algorithm to find the k-th smallest element in an unordered list. It is related to the quicksort sorting algorithm. + *

+ * Worst-case performance О(n2)
+ * Best-case performance О(n)
+ * Average performance O(n)
+ *

+ * @see Quickselect (Wikipedia) + *
+ * @author Justin Wetherell + */ public class QuickSelect { private static final Random RANDOM = new Random(); @@ -38,5 +49,4 @@ else if (value < pivot && iValue < pivot) QuickSelect.temp = null; } } - } diff --git a/src/com/jwetherell/algorithms/search/UpperBound.java b/src/com/jwetherell/algorithms/search/UpperBound.java new file mode 100644 index 00000000..5c6d8a21 --- /dev/null +++ b/src/com/jwetherell/algorithms/search/UpperBound.java @@ -0,0 +1,33 @@ +package com.jwetherell.algorithms.search; + +/** + * Upper bound search algorithm.
+ * Upper bound is kind of binary search algorithm but:
+ * -It returns index of first element which is grater than searched value.
+ * -If searched element is bigger than any array element function returns first index after last element.
+ *
+ * Behaviour for unsorted arrays is unspecified. + *

+ * Complexity O(log n). + *
+ * @author Bartlomiej Drozd + * @author Justin Wetherell + */ +public class UpperBound { + + private UpperBound() { } + + public static int upperBound(int[] array, int length, int value) { + int low = 0; + int high = length; + while (low < high) { + final int mid = (low + high) / 2; + if (value >= array[mid]) { + low = mid + 1; + } else { + high = mid; + } + } + return low; + } +} diff --git a/src/com/jwetherell/algorithms/sequence/ArithmeticProgression.java b/src/com/jwetherell/algorithms/sequence/ArithmeticProgression.java new file mode 100644 index 00000000..50fad654 --- /dev/null +++ b/src/com/jwetherell/algorithms/sequence/ArithmeticProgression.java @@ -0,0 +1,44 @@ +package com.jwetherell.algorithms.sequence; + +/** + * Compute the result of adding a sequence of numbers from N (startNumber) to N+X (startNumber+numberOfNumbersToCompute) + *

+ * @see Arithmetic Progression (Wikipedia) + *
+ * @author Justin Wetherell + */ +public class ArithmeticProgression { + + /** + * Compute the result of adding X (numberOfNumbersToCompute) together starting at N (startNumber). + *

+ * e.g. result = N + (N+1) + (N+2) + (N+3) + ..... + (N+X) + */ + public static final long sequenceTotalUsingLoop(int startNumber, int numberOfNumbersToCompute) { + int start = startNumber; + int length = numberOfNumbersToCompute; + long result = 0L; + while (length > 0) { + result += start++; + length--; + } + return result; + } + + /** + * Compute the result of adding X (numberOfNumbersToCompute) together starting at N (startNumber) using triangular numbers. + *

+ * e.g. result = N + (N+1) + (N+2) + (N+3) + ..... + (N+X)
+ *
+ * @see Triangular Number (Wikipedia) + */ + public static final long sequenceTotalUsingTriangularNumbers(int startNumber, int numberOfNumbersToCompute) { + // n*(n+1)/2 + final int start = startNumber; + final int length = numberOfNumbersToCompute; + + long result = length * (length + 1) / 2; + result += (start - 1) * length; + return result; + } +} diff --git a/src/com/jwetherell/algorithms/sequence/FibonacciSequence.java b/src/com/jwetherell/algorithms/sequence/FibonacciSequence.java index abec64da..7626a42d 100644 --- a/src/com/jwetherell/algorithms/sequence/FibonacciSequence.java +++ b/src/com/jwetherell/algorithms/sequence/FibonacciSequence.java @@ -1,18 +1,22 @@ package com.jwetherell.algorithms.sequence; +/** + * In mathematics, the Fibonacci numbers are the numbers in the following integer sequence, called the Fibonacci sequence, and characterized by the fact that every number after the first two is the + * sum of the two preceding ones: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... + *

+ * @see Fibonacci Sequence (Wikipedia) + *
+ * @author Justin Wetherell + */ public class FibonacciSequence { - private static final double INVERSE_SQUARE_ROOT_OF_5 = 1 / Math.sqrt(5); // Inverse - // of - // the - // square - // root - // of - // 5 + private static final double INVERSE_SQUARE_ROOT_OF_5 = 1 / Math.sqrt(5); // Inverse of the square root of 5 private static final double PHI = (1 + Math.sqrt(5)) / 2; // Golden ratio + private FibonacciSequence() {} + public static final long fibonacciSequenceUsingLoop(int n) { - long[] array = new long[n + 1]; + final long[] array = new long[n + 1]; int counter = 0; while (counter <= n) { long r = 0; @@ -21,6 +25,9 @@ public static final long fibonacciSequenceUsingLoop(int n) { } else if (counter == 1) { r = 1; } + // If r goes below zero then we have run out of bits in the long + if (r < 0) + throw new IllegalArgumentException("Run out of bits in long, n="+n); array[counter] = r; counter++; } @@ -28,15 +35,51 @@ public static final long fibonacciSequenceUsingLoop(int n) { return array[n]; } + /** + * Recursion with memoization + */ public static final long fibonacciSequenceUsingRecursion(int n) { - if (n == 0 || n == 1) return n; - return fibonacciSequenceUsingRecursion(n - 1) + fibonacciSequenceUsingRecursion(n - 2); + // Using the array to store values already computed + final long[] array = new long[n + 1]; + return fibonacciSequenceUsingRecursion(array,n); + } + + private static final long fibonacciSequenceUsingRecursion(long[] array, int n) { + if (n == 0 || n == 1) + return n; + + // If array already has a value then it has previously been computed + if (array[n] != 0) + return array[n]; + + final String exception = "Run out of bits in long, n="+n; + + final long r1 = fibonacciSequenceUsingRecursion(array, (n - 1)); + array[n-1] = r1; // memoization + // If r1 goes below zero then we have run out of bits in the long + if (r1 < 0) + throw new IllegalArgumentException(exception); + + final long r2 = fibonacciSequenceUsingRecursion(array, (n - 2)); + array[n-2] = r2; // memoization + // If r2 goes below zero then we have run out of bits in the long + if (r2 < 0) + throw new IllegalArgumentException(exception); + + final long r = r1 + r2; + // If r goes below zero then we have run out of bits in the long + if (r < 0) + throw new IllegalArgumentException("Run out of bits in long, n="+n); + + array[n] = r; // memoization + + return r; } public static final long fibonacciSequenceUsingMatrixMultiplication(int n) { // m = [ 1 , 1 ] - // [ 1 , 0 ] - long[][] matrix = new long[2][2]; + // [ 1 , 0 ] + final long[][] matrix = new long[2][2]; matrix[0][0] = 1; matrix[0][1] = 1; matrix[1][0] = 1; @@ -52,23 +95,26 @@ public static final long fibonacciSequenceUsingMatrixMultiplication(int n) { while (counter > 0) { temp = multiplyMatrices(matrix, temp); // Subtract an additional 1 the first time in the loop because the - // first multiplication is - // actually n -= 2 since it multiplying two matrices + // first multiplication is actually n -= 2 since it multiplying two matrices counter -= (counter == n) ? 2 : 1; } - return temp[0][1]; + final long r = temp[0][1]; + // If r goes below zero then we have run out of bits in the long + if (r < 0) + throw new IllegalArgumentException("Run out of bits in long, n="+n); + return r; } private static final long[][] multiplyMatrices(long[][] A, long[][] B) { - long a = A[0][0]; - long b = A[0][1]; - long c = A[1][0]; - long d = A[1][1]; + final long a = A[0][0]; + final long b = A[0][1]; + final long c = A[1][0]; + final long d = A[1][1]; - long e = B[0][0]; - long f = B[0][1]; - long g = B[1][0]; - long h = B[1][1]; + final long e = B[0][0]; + final long f = B[0][1]; + final long g = B[1][0]; + final long h = B[1][1]; B[0][0] = a * e + b * g; B[0][1] = a * f + b * h; @@ -79,6 +125,10 @@ private static final long[][] multiplyMatrices(long[][] A, long[][] B) { } public static final long fibonacciSequenceUsingBinetsFormula(int n) { - return (long) Math.floor(Math.pow(PHI, n) * INVERSE_SQUARE_ROOT_OF_5 + 0.5); + final long r = (long) Math.floor(Math.pow(PHI, n) * INVERSE_SQUARE_ROOT_OF_5 + 0.5); + // If r hits max value then we have run out of bits in the long + if (r == Long.MAX_VALUE) + throw new IllegalArgumentException("Run out of bits in long, n="+n); + return r; } } diff --git a/src/com/jwetherell/algorithms/sequence/LargestSumContiguousSubarray.java b/src/com/jwetherell/algorithms/sequence/LargestSumContiguousSubarray.java new file mode 100644 index 00000000..87f8da9c --- /dev/null +++ b/src/com/jwetherell/algorithms/sequence/LargestSumContiguousSubarray.java @@ -0,0 +1,36 @@ +package com.jwetherell.algorithms.sequence; + +/** + * Given an array of integers, we want to find the largest sum of contiguous + * subarray. + *

+ * @see Maximum Subarray Problem (Wikipedia) + *
+ * @author Miguel Stephane KAKANAKOU + * @author Justin Wetherell + */ +public class LargestSumContiguousSubarray { + + private LargestSumContiguousSubarray() { } + + /** + * Largest sum of contiguous subarray using Kadane's algorithm. + * + * @param A + * the given Array of integer + * @return + */ + public static int getLargestSumContiguousSubarray(int[] A) { + if (A == null) + throw new NullPointerException("The given array is null"); + + int max_so_far = A[0]; + int max_ending_here = A[0]; + for (int i = 1; i < A.length; i++) { + max_ending_here = Math.max(A[i], max_ending_here + A[i]); + max_so_far = Math.max(max_so_far, max_ending_here); + } + return max_so_far; + } + +} diff --git a/src/com/jwetherell/algorithms/sequence/LongestCommonSubsequence.java b/src/com/jwetherell/algorithms/sequence/LongestCommonSubsequence.java index 26361e82..66fa392f 100644 --- a/src/com/jwetherell/algorithms/sequence/LongestCommonSubsequence.java +++ b/src/com/jwetherell/algorithms/sequence/LongestCommonSubsequence.java @@ -3,15 +3,39 @@ import java.util.HashSet; import java.util.Set; +/** + * The longest common subsequence (LCS) problem is the problem of finding the longest subsequence common to all sequences in a set of sequences (often just two sequences). It differs from problems + * of finding common substrings: unlike substrings, subsequences are not required to occupy consecutive positions within the original sequences. + *

+ * @see Longest Common Subsequence Problem (Wikipedia) + *
+ * @author Justin Wetherell + */ +@SuppressWarnings("unchecked") public class LongestCommonSubsequence { private static int[][] lengthMatrix = null; private static Set[][] sequenceMatrix = null; - private LongestCommonSubsequence() { + private LongestCommonSubsequence() { } + + public static MatrixPair getLCS(char[] seq1, char[] seq2) { + try { + populateMatrix(seq1, seq2); + + for (int i = 0; i < seq1.length; i++) { + for (int j = 0; j < seq2.length; j++) { + lengthMatrix[i + 1][j + 1] = longestCommonSubsequence(i, j, seq1, seq2); + } + } + + return (new MatrixPair(lengthMatrix, sequenceMatrix)); + } finally { + lengthMatrix = null; + sequenceMatrix = null; + } } - @SuppressWarnings("unchecked") private static void populateMatrix(char[] seq1, char[] seq2) { lengthMatrix = new int[seq1.length + 1][seq2.length + 1]; sequenceMatrix = new HashSet[seq1.length][seq2.length]; @@ -74,63 +98,51 @@ private static void distribute(char c, Set set) { } } - public static MatrixPair getLCS(char[] seq1, char[] seq2) { - populateMatrix(seq1, seq2); - - for (int i = 0; i < seq1.length; i++) { - for (int j = 0; j < seq2.length; j++) { - lengthMatrix[i + 1][j + 1] = longestCommonSubsequence(i, j, seq1, seq2); - } - } - - return (new MatrixPair(lengthMatrix, sequenceMatrix)); - } - public static class MatrixPair { - private int[][] lengthMatrix = null; - private Set[][] sequenceMatrix = null; + private int[][] lenMatrix = null; + private Set[][] seqMatrix = null; public MatrixPair(int[][] lengthMatrix, Set[][] sequenceMatrix) { - this.lengthMatrix = lengthMatrix; - this.sequenceMatrix = sequenceMatrix; + this.lenMatrix = lengthMatrix; + this.seqMatrix = sequenceMatrix; } public int getLongestSequenceLength() { - if (lengthMatrix == null) + if (lenMatrix == null) return 0; - int length1 = lengthMatrix.length; - int length2 = lengthMatrix[length1 - 1].length; - return lengthMatrix[length1 - 1][length2 - 1]; + int length1 = lenMatrix.length; + int length2 = lenMatrix[length1 - 1].length; + return lenMatrix[length1 - 1][length2 - 1]; } public Set getLongestSequences() { - if (sequenceMatrix == null) + if (seqMatrix == null) return (new HashSet()); - int length1 = sequenceMatrix.length; - int length2 = sequenceMatrix[length1 - 1].length; - return sequenceMatrix[length1 - 1][length2 - 1]; + int length1 = seqMatrix.length; + int length2 = seqMatrix[length1 - 1].length; + return seqMatrix[length1 - 1][length2 - 1]; } public int[][] getLengthMatrix() { - return lengthMatrix; + return lenMatrix; } public Set[][] getSequenceMatrix() { - return sequenceMatrix; + return seqMatrix; } public String getLengthMatrixString() { StringBuilder builder = new StringBuilder(); - if (lengthMatrix == null) { + if (lenMatrix == null) { builder.append("Length matrix is NULL.\n"); } else { - for (int i = 0; i < lengthMatrix.length; i++) { - int length = lengthMatrix[i].length; + for (int i = 0; i < lenMatrix.length; i++) { + int length = lenMatrix[i].length; for (int j = 0; j < length; j++) { - int size = lengthMatrix[i][j]; + int size = lenMatrix[i][j]; builder.append(size); if (j < length - 1) builder.append(",\t"); @@ -143,13 +155,13 @@ public String getLengthMatrixString() { public String getSequenceMatrixString() { StringBuilder builder = new StringBuilder(); - if (sequenceMatrix == null) { + if (seqMatrix == null) { builder.append("Sequence matrix is NULL.\n"); } else { - for (int i = 0; i < sequenceMatrix.length; i++) { - int length = sequenceMatrix[i].length; + for (int i = 0; i < seqMatrix.length; i++) { + int length = seqMatrix[i].length; for (int j = 0; j < length; j++) { - Set set = sequenceMatrix[i][j]; + Set set = seqMatrix[i][j]; builder.append(set.toString()); if (j < length - 1) builder.append(", "); diff --git a/src/com/jwetherell/algorithms/sequence/LongestIncreasingSubsequence.java b/src/com/jwetherell/algorithms/sequence/LongestIncreasingSubsequence.java new file mode 100644 index 00000000..598f6885 --- /dev/null +++ b/src/com/jwetherell/algorithms/sequence/LongestIncreasingSubsequence.java @@ -0,0 +1,58 @@ +package com.jwetherell.algorithms.sequence; + +/** + * In computer science, the longest increasing subsequence problem is to find a subsequence of a given sequence in which the subsequence's elements are in sorted order, lowest to highest, and in + * which the subsequence is as long as possible. This subsequence is not necessarily contiguous, or unique. + *

+ * @see Longest Increasing Subsequence Problem (Wikipedia) + *
+ * @author Bartlomiej Drozd + * @author Justin Wetherell + */ +public class LongestIncreasingSubsequence { + + private LongestIncreasingSubsequence() { } + + /** + * Longest increasing subsequence solved using dynamic programming. + */ + public static int[] getLongestIncreasingSubsequence(int[] X) { + final int[] P = new int[X.length]; + final int[] M = new int[X.length+1]; + int L = 0; + for (int i=0; i L) { + // If we found a subsequence longer than any we've found yet, update L + L = newL; + } + } + + // Reconstruct the longest increasing subsequence + final int[] S = new int[L]; + int k = M[L]; + for (int i=L-1; i>=0; i--) { + S[i] = X[k]; + k = P[k]; + } + + return S; + } +} diff --git a/src/com/jwetherell/algorithms/sequence/LongestPalindromicSubsequence.java b/src/com/jwetherell/algorithms/sequence/LongestPalindromicSubsequence.java new file mode 100644 index 00000000..9d870978 --- /dev/null +++ b/src/com/jwetherell/algorithms/sequence/LongestPalindromicSubsequence.java @@ -0,0 +1,64 @@ +package com.jwetherell.algorithms.sequence; + +/** + * A longest palin­dromic sub­se­quence is a sequence that appears in the same + * rel­a­tive order, but not nec­es­sar­ily contiguous(not sub­string) and + * palin­drome in nature. + *

+ * Given a string, find the length of the longest palin­dromic sub­se­quence in it. + *

+ * @see Longest Palin­dromic Sub­se­quence (Wikipedia) + *
+ * @author Miguel Stephane KAKANAKOU + * @author Justin Wetherell + */ +public class LongestPalindromicSubsequence { + + private LongestPalindromicSubsequence() { } + + /** + * Find the length of the longest palin­dromic sub­se­quence in the given + * string s using the dynamic programming approach. + */ + public static int getLongestPalindromeSubsequence(String s) { + if (s == null) + throw new NullPointerException("The given String is null"); + + final int len = s.length(); + final int[][] M = new int[len][len]; + final char[] ch = s.toCharArray(); + + initializeMatrix(M); + fillMatrix(M, ch); + return M[0][len-1]; + } + + private static void initializeMatrix(int[][] M) { + int len = M.length; + for (int i=0; i + * @see Substring occurs in String (GeeksForGeeks) + *
+ * @author Justin Wetherell + */ public class SubsequenceCounter { private static char[] seq = null; private static char[] subseq = null; private static int[][] tbl = null; - private SubsequenceCounter() { - } + private SubsequenceCounter() { } + /** + * Finds the number of times a string occurs as a subsequence in a text. + * + * @param sequence Text to find subsequence in. + * @param subSequence subsequence to find in the text. + * @return Number of times a string occurs as a subsequence in a text + */ public static int getCount(char[] sequence, char[] subSequence) { - seq = sequence; - subseq = subSequence; - tbl = new int[seq.length + 1][subseq.length + 1]; - - for (int row = 0; row < tbl.length; row++) - for (int col = 0; col < tbl[row].length; col++) - tbl[row][col] = countMatches(row, col); - - return tbl[seq.length][subseq.length]; + try { + seq = sequence; + subseq = subSequence; + tbl = new int[seq.length + 1][subseq.length + 1]; + + for (int row = 0; row < tbl.length; row++) + for (int col = 0; col < tbl[row].length; col++) + tbl[row][col] = countMatches(row, col); + + return tbl[seq.length][subseq.length]; + } finally { + seq = null; + subseq = null; + tbl = null; + } } private static int countMatches(int seqDigitsLeft, int subseqDigitsLeft) { @@ -28,13 +47,12 @@ private static int countMatches(int seqDigitsLeft, int subseqDigitsLeft) { if (seqDigitsLeft == 0) return 0; - char currSeqDigit = seq[seq.length - seqDigitsLeft]; - char currSubseqDigit = subseq[subseq.length - subseqDigitsLeft]; + final char currSeqDigit = seq[seq.length - seqDigitsLeft]; + final char currSubseqDigit = subseq[subseq.length - subseqDigitsLeft]; int result = 0; if (currSeqDigit == currSubseqDigit) result += tbl[seqDigitsLeft - 1][subseqDigitsLeft - 1]; - result += tbl[seqDigitsLeft - 1][subseqDigitsLeft]; return result; diff --git a/src/com/jwetherell/algorithms/sequence/TotalOfSequence.java b/src/com/jwetherell/algorithms/sequence/TotalOfSequence.java deleted file mode 100644 index 566092aa..00000000 --- a/src/com/jwetherell/algorithms/sequence/TotalOfSequence.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.jwetherell.algorithms.sequence; - -public class TotalOfSequence { - - public static final long sequenceTotalUsingLoop(int start, int length) { - long result = 0L; - while (length > 0) { - result += start++; - length--; - } - return result; - } - - public static final long sequenceTotalUsingTriangularNumbers(int start, int length) { - // n*(n+1)/2 - long result = length * (length + 1) / 2; - result += (start - 1) * length; - return result; - } - -} diff --git a/src/com/jwetherell/algorithms/sorts/AmericanFlagSort.java b/src/com/jwetherell/algorithms/sorts/AmericanFlagSort.java index ad91dbe4..7015a147 100644 --- a/src/com/jwetherell/algorithms/sorts/AmericanFlagSort.java +++ b/src/com/jwetherell/algorithms/sorts/AmericanFlagSort.java @@ -5,25 +5,30 @@ * distributes items into hundreds of buckets. Non-comparative sorting * algorithms such as radix sort and American flag sort are typically used to * sort large objects such as strings, for which comparison is not a unit-time - * operation. Family: Bucket. Space: In-place. Stable: False. - * - * Average case = O(n*k/d) Worst case = O(n*k/d) Best case = O(n*k/d) NOTE: n is - * the number of digits and k is the average bucket size - * - * http://en.wikipedia.org/wiki/American_flag_sort - * + * operation. + *

+ * Family: Bucket.
+ * Space: In-place.
+ * Stable: False.
+ *

+ * Average case = O(n*k/d)
+ * Worst case = O(n*k/d)
+ * Best case = O(n*k/d)
+ *

+ * NOTE: n is the number of digits and k is the average bucket size + *

+ * @see American Flag Sort (Wikipedia) + *
* @author Justin Wetherell */ public class AmericanFlagSort { private static final int NUMBER_OF_BUCKETS = 10; // 10 for base 10 numbers - private AmericanFlagSort() { - } + private AmericanFlagSort() { } public static Integer[] sort(Integer[] unsorted) { - int numberOfDigits = getMaxNumberOfDigits(unsorted); // Max number of - // digits + int numberOfDigits = getMaxNumberOfDigits(unsorted); // Max number of digits int max = 1; for (int i = 0; i < numberOfDigits - 1; i++) max *= 10; diff --git a/src/com/jwetherell/algorithms/sorts/BubbleSort.java b/src/com/jwetherell/algorithms/sorts/BubbleSort.java index 2a6b7fdc..824e4e17 100644 --- a/src/com/jwetherell/algorithms/sorts/BubbleSort.java +++ b/src/com/jwetherell/algorithms/sorts/BubbleSort.java @@ -5,20 +5,24 @@ * through the list to be sorted, comparing each pair of adjacent items and * swapping them if they are in the wrong order. The pass through the list is * repeated until no swaps are needed, which indicates that the list is sorted. - * - * Family: Exchanging. Space: In-place. Stable: True. - * - * Average case = O(n^2) Worst case = O(n^2) Best case = O(n) - * - * http://en.wikipedia.org/wiki/Bubble_sort - * + *

+ * Family: Exchanging.
+ * Space: In-place.
+ * Stable: True.
+ *

+ * Average case = O(n^2)
+ * Worst case = O(n^2)
+ * Best case = O(n)
+ *

+ * @see Bubble Sort (Wikipedia) + *
* @author Justin Wetherell */ public class BubbleSort> { - private BubbleSort() { - } - + private BubbleSort() { } + //@param unsorted array + //@return sorted array public static > T[] sort(T[] unsorted) { boolean swapped = true; int length = unsorted.length; @@ -34,7 +38,7 @@ public static > T[] sort(T[] unsorted) { } return unsorted; } - + //swapping the value private static > void swap(int index1, int index2, T[] unsorted) { T value = unsorted[index1]; unsorted[index1] = unsorted[index2]; diff --git a/src/com/jwetherell/algorithms/sorts/CountingSort.java b/src/com/jwetherell/algorithms/sorts/CountingSort.java index a52c84f2..31e6a289 100644 --- a/src/com/jwetherell/algorithms/sorts/CountingSort.java +++ b/src/com/jwetherell/algorithms/sorts/CountingSort.java @@ -5,43 +5,48 @@ * to keys that are small integers; that is, it is an integer sorting algorithm. * It operates by counting the number of objects that have each distinct key * value, and using arithmetic on those counts to determine the positions of - * each key value in the output sequence. Family: Counting. Space: An Array of - * length r. Stable: True. - * - * Average case = O(n+r) Worst case = O(n+r) Best case = O(n+r) NOTE: r is the - * range of numbers (0 to r) to be sorted. - * - * http://en.wikipedia.org/wiki/Counting_sort - * + * each key value in the output sequence. + *

+ * Family: Counting.
+ * Space: An Array of length r.
+ * Stable: True.
+ *

+ * Average case = O(n+r)
+ * Worst case = O(n+r)
+ * Best case = O(n+r)
+ *

+ * NOTE: r is the range of numbers (0 to r) to be sorted. + *

+ * @see Counting Sort (Wikipedia) + *
* @author Justin Wetherell */ public class CountingSort { - private CountingSort() { - } + private CountingSort() { } public static Integer[] sort(Integer[] unsorted) { int maxValue = findMax(unsorted); - int[] counts = new int[maxValue + 1]; + int[] counts = new int[maxValue + 1];//counts number of elements updateCounts(unsorted, counts); populateCounts(unsorted, counts); return unsorted; } - + //finding maximum value in unsorted array private static int findMax(Integer[] unsorted) { - int max = Integer.MIN_VALUE; + int max = Integer.MIN_VALUE;//assume minimum value(-2147483648) of interger is maximum for (int i : unsorted) { if (i > max) max = i; } return max; } - + //Incrementing the number of counts in unsorted array private static void updateCounts(Integer[] unsorted, int[] counts) { for (int e : unsorted) counts[e]++; } - + private static void populateCounts(Integer[] unsorted, int[] counts) { int index = 0; for (int i = 0; i < counts.length; i++) { diff --git a/src/com/jwetherell/algorithms/sorts/HeapSort.java b/src/com/jwetherell/algorithms/sorts/HeapSort.java index e6aec3e8..6991347d 100644 --- a/src/com/jwetherell/algorithms/sorts/HeapSort.java +++ b/src/com/jwetherell/algorithms/sorts/HeapSort.java @@ -4,19 +4,23 @@ * Heapsort is a comparison-based sorting algorithm to create a sorted array (or * list), and is part of the selection sort family. Although somewhat slower in * practice on most machines than a well-implemented quicksort, it has the - * advantage of a more favorable worst-case O(n log n) runtime. Family: - * Selection. Space: In-place. Stable: False. - * - * Average case = O(n*log n) Worst case = O(n*log n) Best case = O(n*log n) - * - * http://en.wikipedia.org/wiki/Heap_sort - * + * advantage of a more favorable worst-case O(n log n) runtime. + *

+ * Family: Selection.
+ * Space: In-place.
+ * Stable: False.
+ *

+ * Average case = O(n*log n)
+ * Worst case = O(n*log n)
+ * Best case = O(n*log n)
+ *

+ * @see Heap Sort (Wikipedia) + *
* @author Justin Wetherell */ public class HeapSort> { - private HeapSort() { - } + private HeapSort() { } public static > T[] sort(T[] unsorted) { createHeap(unsorted); @@ -34,24 +38,16 @@ private static > void sortHeap(T[] unsorted) { if (left >= index) // node has no left child break; int right = left + 1; - if (right >= index) { // node has a left child, but no right - // child + if (right >= index) { // node has a left child, but no right child if (unsorted[left].compareTo(unsorted[i]) > 0) - swap(left, i, unsorted); // if - // left - // child - // is - // greater - // than - // node + swap(left, i, unsorted); // if left child is greater than node break; } T ithElement = unsorted[i]; T leftElement = unsorted[left]; T rightElement = unsorted[right]; if (ithElement.compareTo(leftElement) < 0) { // (left > i) - if (unsorted[left].compareTo(rightElement) > 0) { // (left > - // right) + if (unsorted[left].compareTo(rightElement) > 0) { // (left > right) swap(left, i, unsorted); i = left; continue; @@ -83,7 +79,8 @@ private static > void createHeap(T[] unsorted) { } } - private static > int add(int length, T element, T[] unsorted) { + private static > int add(int size, T element, T[] unsorted) { + int length = size; int i = length; unsorted[length++] = element; T e = unsorted[i]; diff --git a/src/com/jwetherell/algorithms/sorts/InsertionSort.java b/src/com/jwetherell/algorithms/sorts/InsertionSort.java index e29fc521..6aedf610 100644 --- a/src/com/jwetherell/algorithms/sorts/InsertionSort.java +++ b/src/com/jwetherell/algorithms/sorts/InsertionSort.java @@ -4,18 +4,23 @@ * Insertion sort is a simple sorting algorithm: a comparison sort in which the * sorted array (or list) is built one entry at a time. It is much less * efficient on large lists than more advanced algorithms such as quicksort, - * heapsort, or merge sort. Family: Insertion. Space: In-place. Stable: True. - * - * Average case = O(n^2) Worst case = O(n^2) Best case = O(n) - * - * http://en.wikipedia.org/wiki/Insertion_sort - * + * heapsort, or merge sort. + *

+ * Family: Insertion.
+ * Space: In-place.
+ * Stable: True.
+ *

+ * Average case = O(n^2)
+ * Worst case = O(n^2)
+ * Best case = O(n)
+ *

+ * @see Insertion Sort (Wikipedia) + *
* @author Justin Wetherell */ public class InsertionSort> { - private InsertionSort() { - } + private InsertionSort() { } public static > T[] sort(T[] unsorted) { int length = unsorted.length; diff --git a/src/com/jwetherell/algorithms/sorts/MergeSort.java b/src/com/jwetherell/algorithms/sorts/MergeSort.java index 38ffcced..69823f81 100644 --- a/src/com/jwetherell/algorithms/sorts/MergeSort.java +++ b/src/com/jwetherell/algorithms/sorts/MergeSort.java @@ -3,32 +3,42 @@ /** * Merge sort is an O(n log n) comparison-based sorting algorithm. Most * implementations produce a stable sort, which means that the implementation - * preserves the input order of equal elements in the sorted output. Family: - * Merging. Space: In-place. Stable: True. - * - * Average case = O(n*log n) Worst case = O(n*log n) Best case = O(n*log n) - * - * http://en.wikipedia.org/wiki/Merge_sort - * + * preserves the input order of equal elements in the sorted output. + *

+ * Family: Merging.
+ * Space: In-place.
+ * Stable: True.
+ *

+ * Average case = O(n*log n)
+ * Worst case = O(n*log n)
+ * Best case = O(n*log n)
+ *

+ * @see Merge Sort (Wikipedia) + *
* @author Justin Wetherell */ +@SuppressWarnings("unchecked") public class MergeSort> { - private MergeSort() { - } + public static enum SPACE_TYPE { IN_PLACE, NOT_IN_PLACE } + + private MergeSort() { } - public static > T[] sort(T[] unsorted) { - sort(0, unsorted.length, unsorted); + public static > T[] sort(SPACE_TYPE type, T[] unsorted) { + sort(type, 0, unsorted.length, unsorted); return unsorted; } - private static > void sort(int start, int length, T[] unsorted) { + private static > void sort(SPACE_TYPE type, int start, int length, T[] unsorted) { if (length > 2) { int aLength = (int) Math.floor(length / 2); int bLength = length - aLength; - sort(start, aLength, unsorted); - sort(start + aLength, bLength, unsorted); - merge(start, aLength, start + aLength, bLength, unsorted); + sort(type, start, aLength, unsorted); + sort(type, start + aLength, bLength, unsorted); + if (type == SPACE_TYPE.IN_PLACE) + mergeInPlace(start, aLength, start + aLength, bLength, unsorted); + else + mergeWithExtraStorage(start, aLength, start + aLength, bLength, unsorted); } else if (length == 2) { T e = unsorted[start + 1]; if (e.compareTo(unsorted[start]) < 0) { @@ -38,8 +48,28 @@ private static > void sort(int start, int length, T[] un } } - @SuppressWarnings("unchecked") - private static > void merge(int aStart, int aLength, int bStart, int bLength, T[] unsorted) { + private static > void mergeInPlace(int aStart, int aLength, int bStart, int bLength, T[] unsorted) { + int i = aStart; + int j = bStart; + int aSize = aStart + aLength; + int bSize = bStart + bLength; + while (i < aSize && j < bSize) { + T a = unsorted[i]; + T b = unsorted[j]; + if (b.compareTo(a) < 0) { + // Shift everything to the right one spot + System.arraycopy(unsorted, i, unsorted, i+1, j-i); + unsorted[i] = b; + i++; + j++; + aSize++; + } else { + i++; + } + } + } + + private static > void mergeWithExtraStorage(int aStart, int aLength, int bStart, int bLength, T[] unsorted) { int count = 0; T[] output = (T[]) new Comparable[aLength + bLength]; int i = aStart; @@ -61,7 +91,7 @@ private static > void merge(int aStart, int aLength, int } else if (b != null && a == null) { output[count++] = b; j++; - } else if (b.compareTo(a) <= 0) { + } else if (b != null && b.compareTo(a) <= 0) { output[count++] = b; j++; } else { diff --git a/src/com/jwetherell/algorithms/sorts/QuickSort.java b/src/com/jwetherell/algorithms/sorts/QuickSort.java index e7593270..7484942b 100644 --- a/src/com/jwetherell/algorithms/sorts/QuickSort.java +++ b/src/com/jwetherell/algorithms/sorts/QuickSort.java @@ -3,42 +3,47 @@ import java.util.Random; /** - * Quicksort is a sorting algorithm which, on average, makes comparisons to sort - * n items. In the worst case, it makes comparisons, though this behavior is - * rare. Quicksort is often faster in practice than other algorithms. Family: - * Divide and conquer. Space: In-place. Stable: False. - * - * Average case = O(n) Worst case = O(n^2) Best case = O(n*log n) - * - * http://en.wikipedia.org/wiki/Quick_sort - * + * Quicksort is a sorting algorithm which, on average, makes O(n*log n) comparisons to sort + * n items. In the worst case, it makes O(n^2) comparisons, though this behavior is + * rare. Quicksort is often faster in practice than other algorithms. + *

+ * Family: Divide and conquer.
+ * Space: In-place.
+ * Stable: False.
+ *

+ * Average case = O(n*log n)
+ * Worst case = O(n^2)
+ * Best case = O(n) [three-way partition and equal keys]
+ *

+ * @see Quicksort (Wikipedia) + *
* @author Justin Wetherell */ public class QuickSort> { - private static final Random RANDOM = new Random(); + private static final Random RAND = new Random(); public static enum PIVOT_TYPE { FIRST, MIDDLE, RANDOM - }; + } public static PIVOT_TYPE type = PIVOT_TYPE.RANDOM; private QuickSort() { } - public static > T[] sort(PIVOT_TYPE type, T[] unsorted) { + public static > T[] sort(PIVOT_TYPE pivotType, T[] unsorted) { int pivot = 0; - if (type == PIVOT_TYPE.MIDDLE) { + if (pivotType == PIVOT_TYPE.MIDDLE) { pivot = unsorted.length/2; - } else if (type == PIVOT_TYPE.RANDOM) { + } else if (pivotType == PIVOT_TYPE.RANDOM) { pivot = getRandom(unsorted.length); } sort(pivot, 0, unsorted.length - 1, unsorted); return unsorted; } - private static > void sort(int pivotIndex, int start, int finish, T[] unsorted) { - pivotIndex = start + pivotIndex; + private static > void sort(int index, int start, int finish, T[] unsorted) { + int pivotIndex = start + index; T pivot = unsorted[pivotIndex]; int s = start; int f = finish; @@ -65,7 +70,7 @@ private static > void sort(int pivotIndex, int start, in private static final int getRandom(int length) { if (type == PIVOT_TYPE.RANDOM && length > 0) - return RANDOM.nextInt(length); + return RAND.nextInt(length); if (type == PIVOT_TYPE.FIRST && length > 0) return 0; return length / 2; diff --git a/src/com/jwetherell/algorithms/sorts/RadixSort.java b/src/com/jwetherell/algorithms/sorts/RadixSort.java index 9506c0f7..299f6fcb 100644 --- a/src/com/jwetherell/algorithms/sorts/RadixSort.java +++ b/src/com/jwetherell/algorithms/sorts/RadixSort.java @@ -8,30 +8,33 @@ * same significant position and value. A positional notation is required, but * because integers can represent strings of characters (e.g., names or dates) * and specially formatted floating point numbers, radix sort is not limited to - * integers. Family: Bucket. Space: 10 Buckets with at most n integers per - * bucket. Stable: True. - * - * Average case = O(n*k) Worst case = O(n*k) Best case = O(n*k) NOTE: n is the - * number of digits and k is the average bucket size - * - * http://en.wikipedia.org/wiki/Radix_sort - * + * integers. + *

+ * Family: Bucket.
+ * Space: 10 Buckets with at most n integers per bucket.
+ * Stable: True.
+ *

+ * Average case = O(n*k)
+ * Worst case = O(n*k)
+ * Best case = O(n*k)
+ *

+ * NOTE: n is the number of digits and k is the average bucket size + *

+ * @see Radix Sort (Wikipedia) + *
* @author Justin Wetherell */ public class RadixSort { private static final int NUMBER_OF_BUCKETS = 10; // 10 for base 10 numbers - private RadixSort() { - } + private RadixSort() { } public static Integer[] sort(Integer[] unsorted) { int[][] buckets = new int[NUMBER_OF_BUCKETS][10]; for (int i = 0; i < NUMBER_OF_BUCKETS; i++) - buckets[i][0] = 1; // Size is one since the size is stored in first - // element - int numberOfDigits = getMaxNumberOfDigits(unsorted); // Max number of - // digits + buckets[i][0] = 1; // Size is one since the size is stored in first element + int numberOfDigits = getMaxNumberOfDigits(unsorted); // Max number of digits int divisor = 1; for (int n = 0; n < numberOfDigits; n++) { int digit = 0; @@ -71,11 +74,12 @@ private static int getDigit(int integer, int divisor) { private static int[] add(int integer, int[] bucket) { int size = bucket[0]; // size is stored in first element int length = bucket.length; + int[] result = bucket; if (size >= length) { - bucket = Arrays.copyOf(bucket, ((length * 3) / 2) + 1); + result = Arrays.copyOf(result, ((length * 3) / 2) + 1); } - bucket[size] = integer; - bucket[0] = ++size; - return bucket; + result[size] = integer; + result[0] = ++size; + return result; } } diff --git a/src/com/jwetherell/algorithms/sorts/ShellSort.java b/src/com/jwetherell/algorithms/sorts/ShellSort.java index 9d1f65fb..c94e7fed 100644 --- a/src/com/jwetherell/algorithms/sorts/ShellSort.java +++ b/src/com/jwetherell/algorithms/sorts/ShellSort.java @@ -9,20 +9,23 @@ * bubble sort, by starting the comparison and exchange of elements with * elements that are far apart before finishing with neighboring elements. * Starting with far apart elements can move some out-of-place elements into - * position faster than a simple nearest neighbor exchange. Family: Exchanging. - * Space: In-place. Stable: False. - * - * Average case = depends on the gap Worst case = O(n * log^2 n) Best case = - * O(n) - * - * http://en.wikipedia.org/wiki/Shell_sort - * + * position faster than a simple nearest neighbor exchange. + *

+ * Family: Exchanging.
+ * Space: In-place.
+ * Stable: False.
+ *

+ * Average case = depends on the gap
+ * Worst case = O(n * log^2 n)
+ * Best case = O(n)
+ *

+ * @see Shell Sort (Wikipedia) + *
* @author Justin Wetherell */ -public abstract class ShellSort> { +public class ShellSort> { - private ShellSort() { - } + private ShellSort() { } public static > T[] sort(int[] shells, T[] unsorted) { for (int gap : shells) { diff --git a/src/com/jwetherell/algorithms/strings/KnuthMorrisPratt.java b/src/com/jwetherell/algorithms/strings/KnuthMorrisPratt.java new file mode 100644 index 00000000..97806084 --- /dev/null +++ b/src/com/jwetherell/algorithms/strings/KnuthMorrisPratt.java @@ -0,0 +1,53 @@ +package com.jwetherell.algorithms.strings; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class implements KMP algorithm for finding length of maximal prefix-suffix for each prefix of the string. + * Prefix-suffix of string S is a substring which occurs at the beginning and at the end of S. + *

+ * Time complexity: O(n)
+ *

+ * @see Knuth Morris Pratt (Wikipedia) + *
+ * @author Szymon Stankiewicz + * @author Justin Wetherell + */ +public class KnuthMorrisPratt { + + private KnuthMorrisPratt() {} + + /** + * This function implements KMP algorithm for finding length of maximal prefix-suffix for each prefix of the string. + * Prefix-suffix of string S is a substring which occurs at the beginning and at the end of S. + *

+ * @param text Text + * @return maximal length of prefix-suffix for each prefix of the string text + */ + public static List getPrefSufTable(String text) { + + final List prefSufTable = new ArrayList(); + final char[] chars = text.toCharArray(); + + if (text.length() == 0) + return prefSufTable; + + prefSufTable.add(0); + + for (int i = 1; i 0 && (chars[i] != chars[sizeOfPrefSuf])) + sizeOfPrefSuf = prefSufTable.get(sizeOfPrefSuf-1); // because string is 0-indexed + + // if characters at this positions are different then sizeOfPrefSuf is equal to zero, + // so there is no proper prefix-suffix + if (chars[i] == chars[sizeOfPrefSuf]) { + prefSufTable.add(sizeOfPrefSuf+1); + } else { + prefSufTable.add(0); + } + } + return prefSufTable; + } +} diff --git a/src/com/jwetherell/algorithms/strings/Manacher.java b/src/com/jwetherell/algorithms/strings/Manacher.java new file mode 100644 index 00000000..eab8d5c4 --- /dev/null +++ b/src/com/jwetherell/algorithms/strings/Manacher.java @@ -0,0 +1,79 @@ +package com.jwetherell.algorithms.strings; + +/** + * The longest palindromic substring or longest symmetric factor problem + * is the problem of finding a maximum-length contiguous substring of a given string that is also a palindrome. + *

+ * The longest palindromic substring problem should not be confused with + * the different problem of finding the longest palindromic subsequence. + *

+ * Manacher's algorithm finds the longest palindromic substring in linear time O(n); where n = length(input) + *

+ * @see Manacher's Algorithm (Wikipedia) + *
+ * @author Piotr Kruk + * @author Justin Wetherell + */ +public class Manacher { + + private Manacher() {} + + /** + * This function implements Manacher's algorithm that finds + * the longest palindromic substring in a linear time + * If there is no unique longest palindromic substring it returns the first one to occur + * + * @param input + * @return the longest palindromic substring in input + */ + public static String getLongestPalindromicSubstring(String input) { + if (input == null) + return null; + + final int length = input.length(); + if (length == 0) + return ""; + + // arr represents input string in a way that will act the same for strings of even and uneven length + // i.e. '#' is placed between each letter from input + final char[] arr = new char[2 * length + 1]; + for (int i = length - 1; i >= 0; i--) { + arr[2 * i + 1] = input.charAt(i); + arr[2 * i] = '#'; + } + arr[2 * length] = '#'; + + final int arrLength = length * 2; + + // LPS[i] - palindrome span(radius) with center at arr[i] + final int[] LPS = new int[arrLength + 1]; + int p = 0; + for (int i = 1; i <= arrLength; i++) { + LPS[i] = 0; + if (LPS[p] + p >= i) + LPS[i] = Math.min(LPS[2 * p - i], p + LPS[p] - i); + while (i + LPS[i] + 1 <= arrLength && i - LPS[i] - 1 >= 0 && arr[i + LPS[i] + 1] == arr[i - LPS[i] - 1]) + LPS[i]++; + if (p + LPS[p] < i + LPS[i]) + p = i; + } + + // find the palindrome with the biggest span + int valueMax = 0; + int indexMax = 0; + for (int i = 0; i < arrLength; i++) { + if (valueMax < LPS[i]) { + valueMax = LPS[i]; + indexMax = i; + } + } + + // reconstruct the palindrome given its index in LPS and span + final int palindromeSpan = valueMax / 2; + if (indexMax % 2 == 0) { + return input.substring(indexMax/2 - palindromeSpan, indexMax/2 + palindromeSpan); + } else { + return input.substring(indexMax/2 - palindromeSpan, indexMax/2 + palindromeSpan + 1); + } + } +} diff --git a/src/com/jwetherell/algorithms/strings/Rotation.java b/src/com/jwetherell/algorithms/strings/Rotation.java new file mode 100644 index 00000000..09d8520d --- /dev/null +++ b/src/com/jwetherell/algorithms/strings/Rotation.java @@ -0,0 +1,94 @@ +package com.jwetherell.algorithms.strings; + +/** + * Rotation of the string is some cyclic transformation of that string. + * More formally a string s = uv is said to be a rotation of t if t = vu. + *

+ * @see String Rotation (Wikipedia) + *
+ * @Author Szymon Stankiewicz + * @author Justin Wetherell + */ +public class Rotation { + + private static char charAt(String text, int pos) { + pos = pos % text.length(); + return text.charAt(pos); + } + + private static int compare(char a, char b, boolean greater) { + if (a == b) + return 0; + return (a < b) ^ greater ? -1 : 1; + } + + private static String bestRotation(String text, boolean greatest) { + if (text.length() < 2) + return text; + + final int n = text.length() * 2; + int k = 0; + int i = 0, j = 1; + while (i + k < n && j + k < n) { + final char a = charAt(text, i+k); + final char b = charAt(text, j+k); + final int comp = compare(a, b, greatest); + if (comp == 0) { + k++; + } else if (comp > 0) { + i += k+1; + if (i <= j ) + i = j + 1; + k = 0; + } else { + j += k+1; + if (j <= i) + j = i + 1; + k = 0; + } + } + final int pos = i < j ? i : j; + return text.substring(pos) + text.substring(0, pos); + } + + /** + * Finds lexicographically minimal string rotation. + * Lexicographically minimal string rotation is a rotation of a string possessing the + * lowest lexicographical order of all such rotations. + * Finding the lexicographically minimal rotation is useful as a way of normalizing strings. + *

+ * @see Lexicographically Minimal String Rotation (Wikipedia) + *

+ * This function implements Duval's algorithm. + *

+ * @see Duval's Algorithm (Wikipedia) + *

+ * Complexity: O(n) + *
+ * @param text + * @return lexicographicall minimal rotation of text + */ + public static String getLexicographicallyMinimalRotation(String text) { + return bestRotation(text, false); + } + + /** + * Finds lexicographically maximal string rotation. + * Lexicographically maximal string rotation is a rotation of a string possessing the + * highest lexicographical order of all such rotations. + * Finding the lexicographically maximal rotation is useful as a way of normalizing strings. + *

+ * @see Lexicographically Minimal String Rotation (Wikipedia) + *

+ * This function implements Duval's algorithm. + * @see Duval's Algorithm (Wikipedia) + *

+ * Complexity: O(n) + *
+ * @param text + * @return lexicographicall minimal rotation of text + */ + public static String getLexicographicallyMaximalRotation(String text) { + return bestRotation(text, true); + } +} diff --git a/src/com/jwetherell/algorithms/strings/StringFunctions.java b/src/com/jwetherell/algorithms/strings/StringFunctions.java index bad40aab..8b9cdb41 100644 --- a/src/com/jwetherell/algorithms/strings/StringFunctions.java +++ b/src/com/jwetherell/algorithms/strings/StringFunctions.java @@ -3,6 +3,11 @@ import java.util.BitSet; import java.util.StringTokenizer; +/** + * This class contains methods for modifying text. + *

+ * @author Justin Wetherell + */ public class StringFunctions { private static final char SPACE = ' '; @@ -16,7 +21,7 @@ public static final String reverseWithStringConcat(String string) { } public static final String reverseWithStringBuilder(String string) { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); for (int i = (string.length() - 1); i >= 0; i--) { builder.append(string.charAt(i)); } @@ -24,14 +29,15 @@ public static final String reverseWithStringBuilder(String string) { } public static final String reverseWithStringBuilderBuiltinMethod(String string) { - StringBuilder builder = new StringBuilder(string); + final StringBuilder builder = new StringBuilder(string); return builder.reverse().toString(); } public static final String reverseWithSwaps(String string) { - char[] array = string.toCharArray(); - int length = array.length - 1; - int half = (int) Math.floor(array.length / 2); + final char[] array = string.toCharArray(); + final int length = array.length - 1; + final int half = (int) Math.floor(array.length / 2); + char c; for (int i = length; i >= half; i--) { c = array[length - i]; @@ -42,9 +48,10 @@ public static final String reverseWithSwaps(String string) { } public static final String reverseWithXOR(String string) { - char[] array = string.toCharArray(); - int length = array.length; - int half = (int) Math.floor(array.length / 2); + final char[] array = string.toCharArray(); + final int length = array.length; + final int half = (int) Math.floor(array.length / 2); + for (int i = 0; i < half; i++) { array[i] ^= array[length - i - 1]; array[length - i - 1] ^= array[i]; @@ -54,13 +61,13 @@ public static final String reverseWithXOR(String string) { } public static final String reverseWordsByCharWithAdditionalStorage(String string) { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); + final int length = string.length() - 1; + final StringBuilder temp = new StringBuilder(); char c = 0; int index = 0; int last = string.length(); - int length = string.length() - 1; - StringBuilder temp = new StringBuilder(); for (int i = length; i >= 0; i--) { c = string.charAt(i); if (c == SPACE || i == 0) { @@ -78,25 +85,23 @@ public static final String reverseWordsByCharWithAdditionalStorage(String string } public static final String reverseWordsUsingStringTokenizerWithAdditionalStorage(String string) { + final StringTokenizer st = new StringTokenizer(string); String output = new String(); - - StringTokenizer st = new StringTokenizer(string); while (st.hasMoreTokens()) { output = (st.nextToken()) + ' ' + output; } - return output; + return output.trim(); } public static final String reverseWordsUsingSplitWithAdditionalStorage(String string) { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); + final String[] temp = string.split(" "); - String[] temp = string.split(" "); for (int i = (temp.length - 1); i >= 0; i--) { - builder.append(temp[i]); + builder.append(temp[i]).append(' '); } - - return builder.toString(); + return builder.toString().trim(); } public static final String reverseWordsInPlace(String string) { @@ -185,14 +190,14 @@ private static final void shiftLeft(int start, int end, char[] array) { } public static final boolean isPalindromeWithAdditionalStorage(String string) { - String reversed = new StringBuilder(string).reverse().toString(); + final String reversed = new StringBuilder(string).reverse().toString(); return string.equals(reversed); } public static final boolean isPalindromeInPlace(String string) { - char[] array = string.toCharArray(); - int length = array.length - 1; - int half = Math.round(array.length / 2); + final char[] array = string.toCharArray(); + final int length = array.length - 1; + final int half = Math.round(array.length / 2f); char a, b; for (int i = length; i >= half; i--) { a = array[length - i]; @@ -203,15 +208,15 @@ public static final boolean isPalindromeInPlace(String string) { return true; } - public static final String[] generateSubsets(String input) { - int length = input.length(); - int size = (int) Math.pow(2, length); - BitSet[] sets = new BitSet[size]; - String[] output = new String[size]; + public static final String[] generateSubsets(String inputString) { + final int length = inputString.length(); + final int size = (int) Math.pow(2, length); + final BitSet[] sets = new BitSet[size]; + final String[] output = new String[size]; for (int i = 0; i < size; i++) { - BitSet set = new BitSet(size); - StringBuilder builder = new StringBuilder(); + final BitSet set = new BitSet(size); + final StringBuilder builder = new StringBuilder(); if (i > 0) { for (int j = length - 1; j >= 0; j--) { if (j == length - 1) { @@ -228,22 +233,21 @@ public static final String[] generateSubsets(String input) { set.set(j, prev); } if (set.get(j)) - builder.append(input.charAt(j)); + builder.append(inputString.charAt(j)); } } sets[i] = set; output[i] = builder.toString(); } - return output; } - public static final int levenshteinDistance(String s, String t) { - int sLength = s.length(); - int tLength = t.length(); - - char[] sChars = s.toCharArray(); - char[] tChars = t.toCharArray(); + /** recursive **/ + public static final int levenshteinDistanceRecursive(String s, String t) { + final int sLength = s.length(); + final int tLength = t.length(); + final char[] sChars = s.toCharArray(); + final char[] tChars = t.toCharArray(); int cost = 0; if ((sLength > 0 && tLength > 0) && sChars[0] != tChars[0]) @@ -254,12 +258,51 @@ public static final int levenshteinDistance(String s, String t) { else if (tLength == 0) return sLength; else { - int min1 = levenshteinDistance(s.substring(1), t) + 1; - int min2 = levenshteinDistance(s, t.substring(1)) + 1; - int min3 = levenshteinDistance(s.substring(1), t.substring(1)) + cost; + final int min1 = levenshteinDistanceRecursive(s.substring(1), t) + 1; + final int min2 = levenshteinDistanceRecursive(s, t.substring(1)) + 1; + final int min3 = levenshteinDistanceRecursive(s.substring(1), t.substring(1)) + cost; int minOfFirstSet = Math.min(min1, min2); return (Math.min(minOfFirstSet, min3)); } } + + /** iterative - dynamic programming **/ + public static final int levenshteinDistanceIterative(String string1, String string2) { + final char[] s = string1.toCharArray(); + final char[] t = string2.toCharArray(); + final int m = s.length; + final int n = t.length; + + // for all i and j, d[i,j] will hold the Levenshtein distance between + // the first i characters of s and the first j characters of t + // note that d has (m+1)*(n+1) values + final int[][] d = new int[m+1][n+1]; + + // source prefixes can be transformed into empty string by + // dropping all characters + for (int i=1; i<=m; i++) + d[i][0] = i; + + // target prefixes can be reached from empty source prefix + // by inserting every character + for (int j=1; j<=n; j++) + d[0][j] = j; + + int substitutionCost; + for (int j=1; j<=n; j++) { + for (int i=1; i<=m; i++) { + if (s[i-1] == t[j-1]) + substitutionCost = 0; + else + substitutionCost = 1; + + int minOfInsAndDel = Math.min(d[i-1][j] + 1, // deletion + d[i][j-1] + 1); // insertion + d[i][j] = Math.min(minOfInsAndDel, // minimum of insert and delete + d[i-1][j-1] + substitutionCost); // substitution + } + } + return d[m][n]; + } } diff --git a/test.txt b/test.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/test/com/jwetherell/algorithms/data_structures/test/AVLTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/AVLTreeTests.java new file mode 100644 index 00000000..5f6a7777 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/AVLTreeTests.java @@ -0,0 +1,31 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.AVLTree; +import com.jwetherell.algorithms.data_structures.BinarySearchTree; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.TreeTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class AVLTreeTests { + + @Test + public void testAVLTree() { + TestData data = Utils.generateTestData(1000); + + String bstName = "AVL Tree"; + BinarySearchTree bst = new AVLTree(); + Collection bstCollection = bst.toCollection(); + + assertTrue(TreeTest.testTree(bst, Integer.class, bstName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(bstCollection, Integer.class, bstName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/AllTests.java b/test/com/jwetherell/algorithms/data_structures/test/AllTests.java new file mode 100644 index 00000000..d74337e6 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/AllTests.java @@ -0,0 +1,53 @@ +package com.jwetherell.algorithms.data_structures.test; + +import org.junit.runner.JUnitCore; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses( { + AVLTreeTests.class, + BinaryHeapTests.class, + BinarySearchTreeTests.class, + BTreeTests.class, + CompactSuffixTrieTests.class, + DisjointSetTests.class, + FenwickTreeTests.class, + GraphTests.class, + HashArrayMappedTreeTests.class, + HashMapTests.class, + ImplicitKeyTreapTests.class, + IntervalTreeTests.class, + KdTreeTests.class, + ListTests.class, + MatrixTests.class, + PatriciaTreeTests.class, + QuadTreeTests.class, + QueueTests.class, + RadixTrieTests.class, + RedBlackTreeTests.class, + SegmentTreeTests.class, + SkipListMapTests.class, + SkipListTests.class, + SplayTreeTests.class, + StackTests.class, + SuffixTreeTests.class, + SuffixTrieTests.class, + TreapTests.class, + TreeMapTests.class, + TrieTests.class, + TrieMapTests.class + } + ) + +public class AllTests { + + /** + * @param args + */ + public static void main(String[] args) { + JUnitCore core = new JUnitCore(); + core.run(AllTests.class); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/BTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/BTreeTests.java new file mode 100644 index 00000000..6b29e068 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/BTreeTests.java @@ -0,0 +1,29 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.BTree; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.TreeTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class BTreeTests { + + @Test + public void testBTree() { + TestData data = Utils.generateTestData(1000); + + String bstName = "B-Tree"; + BTree bst = new BTree(2); + Collection bstCollection = bst.toCollection(); + + assertTrue(TreeTest.testTree(bst, Integer.class, bstName, data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(bstCollection, Integer.class, bstName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/BinaryHeapTests.java b/test/com/jwetherell/algorithms/data_structures/test/BinaryHeapTests.java new file mode 100644 index 00000000..68591215 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/BinaryHeapTests.java @@ -0,0 +1,84 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNull; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.BinaryHeap; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.HeapTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class BinaryHeapTests { + + @Test + public void testMinHeap() { + TestData data = Utils.generateTestData(2500); + + String aNameMin = "Min-Heap [array]"; + BinaryHeap.BinaryHeapArray aHeapMin = new BinaryHeap.BinaryHeapArray(BinaryHeap.Type.MIN); + Collection aCollectionMin = aHeapMin.toCollection(); + assertTrue(HeapTest.testHeap(BinaryHeap.Type.MIN, aHeapMin, Integer.class, aNameMin, + data.unsorted, data.sorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(aCollectionMin, Integer.class, aNameMin, + data.unsorted, data.sorted, data.invalid)); + + BinaryHeap.BinaryHeapArray aHeapNull = new BinaryHeap.BinaryHeapArray(BinaryHeap.Type.MIN); + aHeapNull.add(10); + aHeapNull.add(11); + aHeapNull.clear(); + assertNull(aHeapNull.getHeadValue()); // we expect null here + + String tNameMin = "Min-Heap [tree]"; + BinaryHeap.BinaryHeapTree tHeapMin = new BinaryHeap.BinaryHeapTree(BinaryHeap.Type.MIN); + Collection tCollectionMin = tHeapMin.toCollection(); + assertTrue(HeapTest.testHeap(BinaryHeap.Type.MIN, tHeapMin, Integer.class, tNameMin, + data.unsorted, data.sorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(tCollectionMin, Integer.class, tNameMin, + data.unsorted, data.sorted, data.invalid)); + + BinaryHeap.BinaryHeapTree tHeapNull = new BinaryHeap.BinaryHeapTree(BinaryHeap.Type.MIN); + tHeapNull.add(10); + tHeapNull.add(11); + tHeapNull.clear(); + assertNull(tHeapNull.getHeadValue()); // we expect null here + + } + + @Test + public void testMaxHeap() { + TestData data = Utils.generateTestData(2500); + + String aNameMax = "Max-Heap [array]"; + BinaryHeap.BinaryHeapArray aHeapMax = new BinaryHeap.BinaryHeapArray(BinaryHeap.Type.MAX); + Collection aCollectionMax = aHeapMax.toCollection(); + assertTrue(HeapTest.testHeap(BinaryHeap.Type.MAX, aHeapMax, Integer.class, aNameMax, + data.unsorted, data.sorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(aCollectionMax, Integer.class, aNameMax, + data.unsorted, data.sorted, data.invalid)); + + BinaryHeap.BinaryHeapArray aHeapNull = new BinaryHeap.BinaryHeapArray(BinaryHeap.Type.MAX); + aHeapNull.add(10); + aHeapNull.add(11); + aHeapNull.clear(); + assertNull(aHeapNull.getHeadValue()); // we expect null here + + String lNameMax = "Max-Heap [tree]"; + BinaryHeap.BinaryHeapTree tHeapMax = new BinaryHeap.BinaryHeapTree(BinaryHeap.Type.MAX); + Collection tCollectionMax = tHeapMax.toCollection(); + assertTrue(HeapTest.testHeap(BinaryHeap.Type.MAX, tHeapMax, Integer.class, lNameMax, + data.unsorted, data.sorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(tCollectionMax, Integer.class, lNameMax, + data.unsorted, data.sorted, data.invalid)); + + BinaryHeap.BinaryHeapTree tHeapNull = new BinaryHeap.BinaryHeapTree(BinaryHeap.Type.MAX); + tHeapNull.add(10); + tHeapNull.add(11); + tHeapNull.clear(); + assertNull(tHeapNull.getHeadValue()); // we expect null here + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/BinarySearchTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/BinarySearchTreeTests.java new file mode 100644 index 00000000..d2569a97 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/BinarySearchTreeTests.java @@ -0,0 +1,143 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.BinarySearchTree; +import com.jwetherell.algorithms.data_structures.BinarySearchTree.DepthFirstSearchOrder; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.TreeTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class BinarySearchTreeTests { + + /** + .....4....
+ ..2.....5.
+ 1...3.....
+ */ + private static final BinarySearchTree testBST = new BinarySearchTree(); + static { + testBST.add(4); + testBST.add(2); + testBST.add(5); + testBST.add(1); + testBST.add(3); + } + + @Test + public void testBST() { + TestData data = Utils.generateTestData(1000); + + String bstName = "BST"; + BinarySearchTree bst = new BinarySearchTree(); + Collection bstCollection = bst.toCollection(); + + assertTrue(TreeTest.testTree(bst, Integer.class, bstName, + data.unsorted, data.invalid)); + + assertTrue(JavaCollectionTest.testCollection(bstCollection, Integer.class, bstName, + data.unsorted, data.sorted, data.invalid)); + } + + /** 4 2 5 1 3 */ + @Test + public void testBSF() { + final Integer[] inOrder = testBST.getBFS(); + final Integer[] expectation = new Integer[]{4, 2, 5, 1, 3}; + for (int i=0; i bst = new BinarySearchTree(); + for (int i : data.unsorted) { + bst.add(i); + } + bst.getBFS(); + } + + /** 4 2 1 3 5 */ + @Test + public void testPreOrderDFS() { + final Integer[] inOrder = testBST.getDFS(DepthFirstSearchOrder.preOrder); + final Integer[] expectation = new Integer[]{4, 2, 1, 3, 5}; + for (int i=0; i bst = new BinarySearchTree(); + for (int i : data.unsorted) { + bst.add(i); + } + bst.getDFS(DepthFirstSearchOrder.preOrder); + } + + /** 1 2 3 4 5 */ + @Test + public void testInOrderDFS() { + final Integer[] inOrder = testBST.getDFS(DepthFirstSearchOrder.inOrder); + final Integer[] expectation = new Integer[]{1, 2, 3, 4, 5}; + for (int i=0; i bst = new BinarySearchTree(); + for (int i : data.unsorted) { + bst.add(i); + } + bst.getDFS(DepthFirstSearchOrder.inOrder); + } + + /** 1 3 2 5 4 */ + @Test + public void testPostOrderDFS() { + final Integer[] inOrder = testBST.getDFS(DepthFirstSearchOrder.postOrder); + final Integer[] expectation = new Integer[]{1, 3, 2, 5, 4}; + for (int i=0; i bst = new BinarySearchTree(); + for (int i : data.unsorted) { + bst.add(i); + } + bst.getDFS(DepthFirstSearchOrder.postOrder); + } + +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/CompactSuffixTrieTests.java b/test/com/jwetherell/algorithms/data_structures/test/CompactSuffixTrieTests.java new file mode 100644 index 00000000..f32f3f64 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/CompactSuffixTrieTests.java @@ -0,0 +1,50 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.CompactSuffixTrie; + +public class CompactSuffixTrieTests { + + @Test + public void testCompactSuffixTrie() { + String bookkeeper = "bookkeeper"; + CompactSuffixTrie trie = new CompactSuffixTrie(bookkeeper); + + boolean exists = trie.doesSubStringExist(bookkeeper); + assertTrue("YIKES!! " + bookkeeper + " doesn't exists.", exists); + + String failed = "booker"; + exists = trie.doesSubStringExist(failed); + assertFalse("YIKES!! " + failed + " exists.", exists); + + String pass = "kkee"; + exists = trie.doesSubStringExist(pass); + assertTrue("YIKES!! " + pass + " doesn't exists.", exists); + } + + @Test + public void testCompactSuffixTrie_equals() { + String bookkeeper = "bookkeeper"; + CompactSuffixTrie trie = new CompactSuffixTrie(bookkeeper); + + String bookkeeper_1 = "bookkeeper"; + CompactSuffixTrie trie_1 = new CompactSuffixTrie(bookkeeper_1); + + boolean equal = trie.equals(trie_1); + assertTrue("YIKES!! " + bookkeeper + " and " + bookkeeper_1 + " are not equal.", equal); + + + String failed = "failed"; + trie = new CompactSuffixTrie(failed); + + String failed_1 = "failet"; + trie_1 = new CompactSuffixTrie(failed_1); + + equal = trie.equals(trie_1); + assertFalse("YIKES!! " + failed + " and " + failed_1 + " are equal.", equal); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/DisjointSetTests.java b/test/com/jwetherell/algorithms/data_structures/test/DisjointSetTests.java new file mode 100644 index 00000000..7d80aacf --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/DisjointSetTests.java @@ -0,0 +1,139 @@ +package com.jwetherell.algorithms.data_structures.test; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.DisjointSet; + +@SuppressWarnings("unchecked") +public class DisjointSetTests { + + private static boolean DEBUG = false; + + @Test + public void testDisjointSet1() { + final int max = 10; + final int[] array = new int[max]; + for (int i=0; i[] items = new DisjointSet.Item[array.length]; + for (int i=0; i s = DisjointSet.makeSet(v); + items[i] = s; + final DisjointSet.Item f = DisjointSet.find(s); + Assert.assertTrue(f!=null); + Assert.assertTrue(f.getValue() == v); + } + if (DEBUG) + System.out.println(toString(items)); + + final int half = items.length/2; + + // first half set + DisjointSet.Item first = items[0]; + for (int i=1; i item = items[i]; + first = DisjointSet.union(first, item); + } + if (DEBUG) + System.out.println(toString(items)); + // second half set + DisjointSet.Item second = items[half]; + for (int i=half+1; i item = items[i]; + second = DisjointSet.union(second, item); + } + if (DEBUG) + System.out.println(toString(items)); + + // Make sure all items in first set are actually in first set and vice versa + for (int i=0; i item = items[i]; + final DisjointSet.Item result = DisjointSet.find(item); + Assert.assertTrue(result.equals(first)); + } + for (int i=half; i item = items[i]; + final DisjointSet.Item result = DisjointSet.find(item); + Assert.assertTrue(result.equals(second)); + } + + // union the second set and the first set and make sure the superset is created properly + DisjointSet.Item superSet = first; + for (int i=0; i item = items[i]; + superSet = DisjointSet.union(superSet, item); + } + if (DEBUG) + System.out.println(toString(items)); + for (int i=0; i item = items[i]; + final DisjointSet.Item result = DisjointSet.find(item); + Assert.assertTrue(result.equals(superSet)); + } + } + + @Test + public void testDisjointSet2() { + final int max = 10; + final int[] array = new int[max]; + for (int i=0; i[] items = new DisjointSet.Item[array.length]; + for (int i=0; i s = DisjointSet.makeSet(v); + items[i] = s; + final DisjointSet.Item f = DisjointSet.find(s); + Assert.assertTrue(f!=null); + Assert.assertTrue(f.getValue() == v); + } + if (DEBUG) + System.out.println(toString(items)); + + final int quarter = items.length/4; + + DisjointSet.Item i1 = items[0]; + for (int i=1; i item = items[i]; + i1 = DisjointSet.union(i1, item); + } + DisjointSet.Item i2 = items[quarter]; + for (int i=quarter+1; i<2*quarter; i++) { + final DisjointSet.Item item = items[i]; + i2 = DisjointSet.union(i2, item); + } + DisjointSet.Item i3 = items[2*quarter]; + for (int i=2*quarter+1; i<3*quarter; i++) { + final DisjointSet.Item item = items[i]; + i3 = DisjointSet.union(i3, item); + } + DisjointSet.Item i4 = items[3*quarter]; + for (int i=3*quarter+1; i item = items[i]; + i4 = DisjointSet.union(i4, item); + } + if (DEBUG) + System.out.println(toString(items)); + + DisjointSet.Item s1 = DisjointSet.union(i1, i2); + DisjointSet.Item s2 = DisjointSet.union(i3, i4); + DisjointSet.Item s3 = DisjointSet.union(s1, s2); + Assert.assertTrue(s3.getValue()==1 && s3.getRank()==3); + if (DEBUG) + System.out.println(toString(items)); + } + + private static final String toString(DisjointSet.Item[] items) { + final StringBuilder builder = new StringBuilder(); + builder.append("{\n"); + for (DisjointSet.Item item : items) { + builder.append('\t').append(item.toString()).append('\n'); + } + builder.append("}\n"); + return builder.toString(); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/FenwickTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/FenwickTreeTests.java new file mode 100644 index 00000000..e635398b --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/FenwickTreeTests.java @@ -0,0 +1,139 @@ +package com.jwetherell.algorithms.data_structures.test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.FenwickTree; +import com.jwetherell.algorithms.data_structures.FenwickTree.Data; + +public class FenwickTreeTests { + + @Test + public void testRangeSumFenwickTree() { + List> segments = new ArrayList>(); + segments.add(new FenwickTree.Data.RangeSumData(0, (Integer) 4)); + segments.add(new FenwickTree.Data.RangeSumData(1, (Integer) 2)); + segments.add(new FenwickTree.Data.RangeSumData(2, (Integer) 6)); + segments.add(new FenwickTree.Data.RangeSumData(3, (Integer) 3)); + segments.add(new FenwickTree.Data.RangeSumData(4, (Integer) 1)); + segments.add(new FenwickTree.Data.RangeSumData(5, (Integer) 5)); + segments.add(new FenwickTree.Data.RangeSumData(6, (Integer) 0)); + segments.add(new FenwickTree.Data.RangeSumData(17, (Integer) 7)); + + // No matter which order the data is given, all tests should pass + + // Initial order. + testRangeSumFenwickTree(segments); + + // Randomize it + Collections.shuffle(segments); + testRangeSumFenwickTree(segments); + + // Try in order + Collections.sort(segments); + testRangeSumFenwickTree(segments); + + // Try reverse order + Collections.sort(segments,REVERSE); + testRangeSumFenwickTree(segments); + } + + private void testRangeSumFenwickTree(List> segments) { // Range Sum Segment tree + FenwickTree> tree = new FenwickTree>(segments); + + FenwickTree.Data.RangeSumData query = tree.query(0, 8); + assertTrue("Segment tree query error. query=0->8 result="+query, tree, query.sum==21); + + query = tree.query(0, 17); + assertTrue("Segment tree query error. query=0->17 result="+query, tree, query.sum==28); + + query = tree.query(2, 5); + assertTrue("Segment tree query error. query=2->5 result="+query, tree, query.sum==15); + + query = tree.query(10, 17); + assertTrue("Segment tree query error. query=10->17 result="+query, tree, query.sum==7); + + query = tree.query(16); // stabbing + assertTrue("Segment tree query error. query=16 result="+query, tree, query.sum==0); + + query = tree.query(17); // stabbing + assertTrue("Segment tree query error. query=17 result="+query, tree, query.sum==7); + } + + @Test + public void testRangeSumFenwickTree2() { + List> segments = new ArrayList>(); + segments.add(new FenwickTree.Data.RangeSumData(0, (Integer) 2)); + segments.add(new FenwickTree.Data.RangeSumData(1, (Integer) 1)); + segments.add(new FenwickTree.Data.RangeSumData(2, (Integer) 1)); + segments.add(new FenwickTree.Data.RangeSumData(3, (Integer) 3)); + segments.add(new FenwickTree.Data.RangeSumData(4, (Integer) 2)); + segments.add(new FenwickTree.Data.RangeSumData(5, (Integer) 3)); + segments.add(new FenwickTree.Data.RangeSumData(6, (Integer) 4)); + segments.add(new FenwickTree.Data.RangeSumData(7, (Integer) 5)); + segments.add(new FenwickTree.Data.RangeSumData(8, (Integer) 6)); + segments.add(new FenwickTree.Data.RangeSumData(9, (Integer) 7)); + segments.add(new FenwickTree.Data.RangeSumData(10, (Integer) 8)); + segments.add(new FenwickTree.Data.RangeSumData(11, (Integer) 9)); + + // No matter which order the data is given, all tests should pass + + // Initial order. + testRangeSumFenwickTree2(segments); + + // Randomize it + Collections.shuffle(segments); + testRangeSumFenwickTree2(segments); + + // Try in order + Collections.sort(segments); + testRangeSumFenwickTree2(segments); + + // Try reverse order + Collections.sort(segments,REVERSE); + testRangeSumFenwickTree2(segments); + } + + private void testRangeSumFenwickTree2(List> segments) { // Range Sum Segment tree + FenwickTree> tree = new FenwickTree>(segments); + + FenwickTree.Data.RangeSumData query = tree.query(0, 8); + assertTrue("Segment tree query error. query=0->8 result="+query, tree, query.sum==27); + + query = tree.query(0, 11); + assertTrue("Segment tree query error. query=0->11 result="+query, tree, query.sum==51); + + query = tree.query(2, 5); + assertTrue("Segment tree query error. query=2->5 result="+query, tree, query.sum==9); + + query = tree.query(10, 17); + assertTrue("Segment tree query error. query=10->17 result="+query, tree, query.sum==17); + + query = tree.query(2); // stabbing + assertTrue("Segment tree query error. query=2 result="+query, tree, query.sum==1); + + query = tree.query(10); // stabbing + assertTrue("Segment tree query error. query=10 result="+query, tree, query.sum==8); + } + + private static final Comparator REVERSE = new Comparator() { + @Override + public int compare(Data arg0, Data arg1) { + int r = arg0.compareTo(arg1); + return r*-1; + } + }; + + // Assertion which won't call toString on the tree unless the assertion fails + private static final void assertTrue(String msg, FenwickTree obj, boolean isTrue) { + String toString = ""; + if (isTrue==false) + toString = "\n"+obj.toString(); + Assert.assertTrue(msg+toString, isTrue); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/GraphTests.java b/test/com/jwetherell/algorithms/data_structures/test/GraphTests.java new file mode 100644 index 00000000..6deb8efd --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/GraphTests.java @@ -0,0 +1,130 @@ +package com.jwetherell.algorithms.data_structures.test; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.data_structures.Graph.Edge; +import com.jwetherell.algorithms.data_structures.Graph.TYPE; +import com.jwetherell.algorithms.data_structures.Graph.Vertex; + +public class GraphTests { + + @Test + public void testVertex() { + final Vertex p1 = new Vertex(10,1); + final Vertex p2 = new Vertex(10,2); + final Vertex p3 = new Vertex(20,1); + final Vertex p4 = new Vertex(20,2); + final Vertex p5 = new Vertex(10,1); + + Assert.assertFalse(p1.equals(p2)); + Assert.assertFalse(p2.equals(p1)); + Assert.assertFalse(p1.equals(p3)); + Assert.assertFalse(p3.equals(p1)); + Assert.assertFalse(p1.equals(p4)); + Assert.assertFalse(p4.equals(p1)); + + Assert.assertTrue(p1.equals(p5) && p1.hashCode()==p5.hashCode()); + Assert.assertTrue(p5.equals(p1) && p5.hashCode()==p1.hashCode()); + } + + @Test + public void testEdge() { + final Vertex p1 = new Vertex(10,1); + final Vertex p2 = new Vertex(10,2); + final Vertex p3 = new Vertex(20,1); + final Vertex p4 = new Vertex(10,1); + + final Edge e1 = new Edge(1,p1,p2); + final Edge e2 = new Edge(1,p2,p1); + final Edge e3 = new Edge(2,p1,p2); + final Edge e4 = new Edge(1,p1,p3); + final Edge e5 = new Edge(1,p4,p2); + + Assert.assertFalse(e1.equals(e2)); + Assert.assertFalse(e2.equals(e1)); + Assert.assertFalse(e1.equals(e3)); + Assert.assertFalse(e3.equals(e1)); + Assert.assertFalse(e1.equals(e3)); + Assert.assertFalse(e3.equals(e1)); + Assert.assertFalse(e1.equals(e4)); + Assert.assertFalse(e4.equals(e1)); + + Assert.assertTrue(e1.equals(e5) && e1.hashCode()==e5.hashCode()); + Assert.assertTrue(e5.equals(e1) && e5.hashCode()==e1.hashCode()); + } + + @Test + public void testGraph() { + final List> vertices = new ArrayList>(); + final Vertex p1 = new Vertex(10,1); + vertices.add(p1); + final Vertex p2 = new Vertex(10,2); + vertices.add(p2); + final Vertex p3 = new Vertex(20,1); + vertices.add(p3); + final Vertex p4 = new Vertex(10,2); + vertices.add(p4); + + final List> edges = new ArrayList>(); + final Edge e1 = new Edge(1,p1,p2); + edges.add(e1); + final Edge e2 = new Edge(1,p2,p1); + edges.add(e2); + final Edge e3 = new Edge(2,p1,p2); + edges.add(e3); + final Edge e4 = new Edge(1,p1,p3); + edges.add(e4); + final Edge e5 = new Edge(1,p4,p2); + edges.add(e5); + + final Graph graph = new Graph(TYPE.DIRECTED, vertices, edges); + final Graph clone = new Graph(graph); + + Assert.assertTrue(graph.equals(clone) && graph.hashCode()==clone.hashCode()); + } + + @Test + public void testCostVertexPair() { + final Graph.CostVertexPair p1 = new Graph.CostVertexPair(1, new Vertex(10)); + final Graph.CostVertexPair p2 = new Graph.CostVertexPair(1, new Vertex(11)); + final Graph.CostVertexPair p3 = new Graph.CostVertexPair(2, new Vertex(10)); + final Graph.CostVertexPair p4 = new Graph.CostVertexPair(1, new Vertex(10)); + + Assert.assertFalse(p1.equals(p2)); + Assert.assertFalse(p2.equals(p1)); + Assert.assertFalse(p1.equals(p3)); + Assert.assertFalse(p3.equals(p1)); + + Assert.assertTrue(p1.equals(p4) && p1.hashCode()==p4.hashCode()); + Assert.assertTrue(p4.equals(p1) && p4.hashCode()==p1.hashCode()); + } + + @Test + public void testCostPathPair() { + final List> s1 = new ArrayList>(3); + s1.add(new Edge(1, new Vertex(10), new Vertex(20))); + s1.add(new Edge(2, new Vertex(20), new Vertex(30))); + final Graph.CostPathPair p1 = new Graph.CostPathPair(1, s1); + + final List> s2 = new ArrayList>(3); + s2.add(new Edge(2, new Vertex(10), new Vertex(20))); + s2.add(new Edge(1, new Vertex(20), new Vertex(30))); + final Graph.CostPathPair p2 = new Graph.CostPathPair(1, s2); + + final List> s3 = new ArrayList>(3); + s3.add(new Edge(2, new Vertex(10), new Vertex(20))); + s3.add(new Edge(1, new Vertex(20), new Vertex(30))); + final Graph.CostPathPair p3 = new Graph.CostPathPair(1, s3); + + Assert.assertFalse(p1.equals(p2)); + Assert.assertFalse(p2.equals(p1)); + + Assert.assertTrue(p2.equals(p3) && p2.hashCode()==p3.hashCode()); + Assert.assertTrue(p3.equals(p2) && p3.hashCode()==p2.hashCode()); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/HashArrayMappedTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/HashArrayMappedTreeTests.java new file mode 100644 index 00000000..f9549cb3 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/HashArrayMappedTreeTests.java @@ -0,0 +1,28 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.HashArrayMappedTrie; +import com.jwetherell.algorithms.data_structures.test.common.JavaMapTest; +import com.jwetherell.algorithms.data_structures.test.common.MapTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class HashArrayMappedTreeTests { + + @Test + public void testHAMT() { + TestData data = Utils.generateTestData(1000); + + String mapName = "HAMT"; + HashArrayMappedTrie map = new HashArrayMappedTrie(); + java.util.Map jMap = map.toMap(); + + assertTrue(MapTest.testMap(map, Integer.class, mapName, + data.unsorted, data.invalid)); + assertTrue(JavaMapTest.testJavaMap(jMap, Integer.class, mapName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/HashMapTests.java b/test/com/jwetherell/algorithms/data_structures/test/HashMapTests.java new file mode 100644 index 00000000..50bfaf47 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/HashMapTests.java @@ -0,0 +1,33 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.HashMap; +import com.jwetherell.algorithms.data_structures.test.common.JavaMapTest; +import com.jwetherell.algorithms.data_structures.test.common.MapTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class HashMapTests { + + @Test + public void testHashMap() { + TestData data = Utils.generateTestData(1000); + + String mapName = "ProbingHashMap"; + HashMap map = new HashMap(HashMap.Type.PROBING); + java.util.Map jMap = map.toMap(); + + assertTrue(MapTest.testMap(map, Integer.class, mapName, data.unsorted, data.invalid)); + assertTrue(JavaMapTest.testJavaMap(jMap, Integer.class, mapName, data.unsorted, data.sorted, data.invalid)); + + mapName = "LinkingHashMap"; + map = new HashMap(HashMap.Type.CHAINING); + jMap = map.toMap(); + + assertTrue(MapTest.testMap(map, Integer.class, mapName, data.unsorted, data.invalid)); + assertTrue(JavaMapTest.testJavaMap(jMap, Integer.class, mapName, data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/ImplicitKeyTreapTests.java b/test/com/jwetherell/algorithms/data_structures/test/ImplicitKeyTreapTests.java new file mode 100644 index 00000000..2ec109cd --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/ImplicitKeyTreapTests.java @@ -0,0 +1,220 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.ImplicitKeyTreap; +import com.jwetherell.algorithms.data_structures.ImplicitKeyTreap.Node; +import com.jwetherell.algorithms.data_structures.ImplicitKeyTreap.Pair; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.ListTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class ImplicitKeyTreapTests { + + @Test + public void testCollection() { + TestData data = Utils.generateTestData(100); + + String name = "ImplicitKeyTreap"; + ImplicitKeyTreap list = new ImplicitKeyTreap(); + assertTrue(ListTest.testList(list, name, data.unsorted, data.invalid)); + + Collection tCollectionMin = list.toCollection(); + assertTrue(JavaCollectionTest.testCollection(tCollectionMin, Integer.class, name, + data.unsorted, data.sorted, data.invalid)); + } + + @Test + public void implicitKeyInsertTests() { + final int[] data = new int[]{1, 2, 3, 4, 5, 6, 7, 8}; + final MyTreap treap = new MyTreap(); + for (int i=0; i treap = new MyTreap(); + for (int i=0; i treap = new MyTreap(); + for (int i=0; i treap = new MyTreap(); + for (int i=0; i> void moveToFront(MyTreap t, int begin, int end) { + Pair p = t.split(end); + Node e = p.getGreater(); + Node tmp = p.getLesser(); + + p = ImplicitKeyTreap.split(tmp, begin); + Node m = p.getLesser(); + Node b = p.getGreater(); + + Node n = ImplicitKeyTreap.merge(b,m); + n = ImplicitKeyTreap.merge(n,e); + + // update treap + t.setRoot(n); + } + + private static > void moveToBack(MyTreap t, int begin, int end) { + Pair p = t.split(end); + Node m = p.getGreater(); + Node tmp = p.getLesser(); + + p = ImplicitKeyTreap.split(tmp, begin); + Node b = p.getLesser(); + Node e = p.getGreater(); + + Node n = ImplicitKeyTreap.merge(b,m); + n = ImplicitKeyTreap.merge(n,e); + + // update treap + t.setRoot(n); + } + + @SuppressWarnings("unused") + private static String toString(Class type, Object[] array) { + final StringBuilder builder = new StringBuilder(); + for (Object a : array) + builder.append(a.toString()).append(" "); + return builder.toString(); + } + + // Opening up the access to the root node + private class MyTreap> extends ImplicitKeyTreap { + + public void setRoot(Node root) { + this.root = root; + } + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/IntervalSumTest.java b/test/com/jwetherell/algorithms/data_structures/test/IntervalSumTest.java new file mode 100644 index 00000000..2203ef02 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/IntervalSumTest.java @@ -0,0 +1,91 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.IntervalSum; + +public class IntervalSumTest { + + @Test + public void properSumAllElementsTest() { + final IntervalSum sub = new IntervalSum(); + for (int i = 0; i<=100; i++) + sub.add(i); + for (int i = 0; i<=100; i++) + assertEquals(i*(i+1)/2, sub.sum(i)); + assertEquals(100*101/2, sub.sum()); + } + + @Test + public void randomGeneratedTest() { + final Random generator = new Random(42); + final List list = new ArrayList(); + for (int i = 0; i<=100; i++) + list.add(i); + final IntervalSum sum = new IntervalSum(list); + for (int i = 0; i<1000000; i++) { + final int pos = generator.nextInt(100); + final int val = generator.nextInt(2000000) - 1000000; + sum.set(pos, val); + list.set(pos, val); + assertEquals(val, sum.get(pos)); + } + + int s = 0; + final List prefSum = new ArrayList(); + prefSum.add(s); + for (Integer val: list) { + s += val; + prefSum.add(s); + } + + for (int i = 0; i<100; i++) { + for (int j = i; j<100; j++) { + assertEquals(prefSum.get(j+1) - prefSum.get(i), sum.sum(i, j)); + } + } + } + + @Test + public void setIndexOutOfRangeTest() { + final IntervalSum sum = new IntervalSum(100); + boolean thrown = false; + try { + sum.set(101, 10); + } catch (IndexOutOfBoundsException e) { + thrown = true; + } + assertTrue(thrown); + } + + @Test + public void sumIndexOutOfRangeTest() { + final IntervalSum sum = new IntervalSum(100); + boolean thrown = false; + try { + sum.sum(101); + } catch (IndexOutOfBoundsException e) { + thrown = true; + } + assertTrue(thrown); + } + + @Test + public void endBeforeStartTest() { + final IntervalSum sum = new IntervalSum(100); + boolean thrown = false; + try { + sum.sum(101, 100); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/IntervalTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/IntervalTreeTests.java new file mode 100644 index 00000000..0006176f --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/IntervalTreeTests.java @@ -0,0 +1,222 @@ +package com.jwetherell.algorithms.data_structures.test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.IntervalTree; + +public class IntervalTreeTests { + + private static boolean collectionsEqual(Collection c1, Collection c2) { + if (c1.size()!=c2.size()) return false; + return c1.containsAll(c2) && c2.containsAll(c1); + } + + private static final Comparator> REVERSE = new Comparator>() { + @Override + public int compare(IntervalTree.IntervalData arg0, IntervalTree.IntervalData arg1) { + int r = arg0.compareTo(arg1); + return r*-1; + } + }; + + final String stravinsky = "Stravinsky"; + final String schoenberg = "Schoenberg"; + final String grieg = "Grieg"; + final String schubert = "Schubert"; + final String mozart = "Mozart"; + final String schuetz = "Schuetz"; + + @Test + public void testLifespanIntervalTree() { + java.util.List> intervals = new ArrayList>(); + intervals.add((new IntervalTree.IntervalData(1888, 1971, stravinsky))); + intervals.add((new IntervalTree.IntervalData(1874, 1951, schoenberg))); + intervals.add((new IntervalTree.IntervalData(1843, 1907, grieg))); + intervals.add((new IntervalTree.IntervalData(1779, 1828, schubert))); + intervals.add((new IntervalTree.IntervalData(1756, 1791, mozart))); + intervals.add((new IntervalTree.IntervalData(1585, 1672, schuetz))); + + // No matter which order the data is given, all tests should pass + + // Initial order. + testLifespanIntervalTree(intervals); + + // Randomize it + Collections.shuffle(intervals); + testLifespanIntervalTree(intervals); + + // Try in order + Collections.sort(intervals); + testLifespanIntervalTree(intervals); + + // Try reverse order + Collections.sort(intervals,REVERSE); + testLifespanIntervalTree(intervals); + } + + public void testLifespanIntervalTree(java.util.List> intervals) { // Lifespan Interval tree + IntervalTree tree = new IntervalTree(intervals); + + IntervalTree.IntervalData query = tree.query(1890); // Stabbing query + assertTrue("Interval Tree query error. query=1890 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(stravinsky, schoenberg, grieg))); + + query = tree.query(1909); // Stabbing query + assertTrue("Interval Tree query error. query=1909 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(stravinsky, schoenberg))); + + query = tree.query(1792, 1903); // Range query + assertTrue("Interval Tree query error. query=1792->1903 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(stravinsky, schoenberg, grieg, schubert))); + + query = tree.query(1776, 1799); // Range query + assertTrue("Interval Tree query error. query=1776->1799 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(schubert, mozart))); + } + + final String RED = "RED"; + final String ORANGE = "ORANGE"; + final String GREEN = "GREEN"; + final String DARK_GREEN = "DARK_GREEN"; + final String BLUE = "BLUE"; + final String PURPLE = "PURPLE"; + final String BLACK = "BLACK"; + + @Test + public void testIntervalTree() { + java.util.List> intervals = new java.util.ArrayList>(); + intervals.add((new IntervalTree.IntervalData(2, 6, RED))); + intervals.add((new IntervalTree.IntervalData(3, 5, ORANGE))); + intervals.add((new IntervalTree.IntervalData(4, 11, GREEN))); + intervals.add((new IntervalTree.IntervalData(5, 10, DARK_GREEN))); + intervals.add((new IntervalTree.IntervalData(8, 12, BLUE))); + intervals.add((new IntervalTree.IntervalData(9, 14, PURPLE))); + intervals.add((new IntervalTree.IntervalData(13, 15, BLACK))); + + // No matter which order the data is given, all tests should pass + + // Initial order. + testIntervalTree(intervals); + + // Randomize it + Collections.shuffle(intervals); + testIntervalTree(intervals); + + // Try in order + Collections.sort(intervals); + testIntervalTree(intervals); + + // Try reverse order + Collections.sort(intervals,REVERSE); + testIntervalTree(intervals); + } + + private void testIntervalTree(java.util.List> intervals) { + IntervalTree tree = new IntervalTree(intervals); + + IntervalTree.IntervalData query = tree.query(2); // Stabbing query + assertTrue("Interval Tree query error. query=2 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(RED))); + + query = tree.query(4); // Stabbing query + assertTrue("Interval Tree query error. query=4 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(RED, ORANGE, GREEN))); + + query = tree.query(9); // Stabbing query + assertTrue("Interval Tree query error. query=9 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(GREEN, DARK_GREEN, BLUE, PURPLE))); + + query = tree.query(1, 16); // Range query + assertTrue("Interval Tree query error. query=1->16 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(RED, ORANGE, GREEN, DARK_GREEN, BLUE, PURPLE, BLACK))); + + query = tree.query(7, 14); // Range query + assertTrue("Interval Tree query error. query=7->14 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(GREEN, DARK_GREEN, BLUE, PURPLE, BLACK))); + + query = tree.query(14, 15); // Range query + assertTrue("Interval Tree query error. query=14->15 returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList(PURPLE, BLACK))); + } + + @Test + public void testIntervalTree2() { + List> intervals = new ArrayList>(); + intervals.add((new IntervalTree.IntervalData(1, 5, "a"))); + intervals.add((new IntervalTree.IntervalData(2, 6, "b"))); + intervals.add((new IntervalTree.IntervalData(3, 7, "c"))); + intervals.add((new IntervalTree.IntervalData(7, 7, "d"))); + intervals.add((new IntervalTree.IntervalData(8, 8, "e"))); + + IntervalTree tree = new IntervalTree(intervals); + + IntervalTree.IntervalData query = tree.query(5); // Stabbing query + assertTrue("Interval Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("b","c","a"))); + + query = tree.query(6); // Stabbing query + assertTrue("Interval Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("b","c"))); + + query = tree.query(7); // Stabbing query + assertTrue("Interval Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("c","d"))); + + query = tree.query(1,7); // Range query + assertTrue("Interval Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("a","b","c","d"))); + + query = tree.query(8); // Stabbing query + assertTrue("Interval Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("e"))); + } + + @Test + public void testIntervalTree3() { + List> intervals = new ArrayList>(); + intervals.add((new IntervalTree.IntervalData(5, 20, "a"))); + intervals.add((new IntervalTree.IntervalData(10, 30, "b"))); + intervals.add((new IntervalTree.IntervalData(12, 15, "c"))); + intervals.add((new IntervalTree.IntervalData(15, 20, "d"))); + intervals.add((new IntervalTree.IntervalData(17, 19, "e"))); + intervals.add((new IntervalTree.IntervalData(30, 40, "f"))); + + IntervalTree tree = new IntervalTree(intervals); + + IntervalTree.IntervalData query = tree.query(6,7); // Range query + assertTrue("Interval Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("a"))); + } + + @Test + public void testIntervalTree4() { + List> intervals = new ArrayList>(); + intervals.add((new IntervalTree.IntervalData(15, 20, "a"))); + intervals.add((new IntervalTree.IntervalData(4, 25, "b"))); + intervals.add((new IntervalTree.IntervalData(3, 30, "c"))); + + IntervalTree tree = new IntervalTree(intervals); + + IntervalTree.IntervalData query = tree.query(26,27); // Range query + assertTrue("Interval Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("c"))); + } + + @Test + public void testIntervalTree5() { + List> intervals = new ArrayList>(); + intervals.add((new IntervalTree.IntervalData(17, 19, "a"))); + intervals.add((new IntervalTree.IntervalData(5, 11, "b"))); + intervals.add((new IntervalTree.IntervalData(23, 23, "c"))); + intervals.add((new IntervalTree.IntervalData(4, 8, "d"))); + intervals.add((new IntervalTree.IntervalData(15, 18, "e"))); + intervals.add((new IntervalTree.IntervalData(7, 10, "f"))); + + IntervalTree tree = new IntervalTree(intervals); + + IntervalTree.IntervalData query = tree.query(14,16); // Range query + assertTrue("Interval Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("e"))); + + query = tree.query(12,14); // Range query + assertTrue("Interval Tree query error. returned=" + query, tree, query==null); + } + + // Assertion which won't call toString on the tree unless the assertion fails + private static final void assertTrue(String msg, IntervalTree obj, boolean isTrue) { + String toString = ""; + if (isTrue==false) + toString = "\n"+obj.toString(); + Assert.assertTrue(msg+toString, isTrue); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/KdTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/KdTreeTests.java new file mode 100644 index 00000000..315bce68 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/KdTreeTests.java @@ -0,0 +1,72 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.KdTree; +import com.jwetherell.algorithms.data_structures.KdTree.XYZPoint; + +public class KdTreeTests { + + @Test + public void testKdTree() { + List points = new ArrayList(); + XYZPoint p1 = new XYZPoint(2, 3); + points.add(p1); + XYZPoint p2 = new XYZPoint(5, 4); + points.add(p2); + XYZPoint p3 = new XYZPoint(9, 6); + points.add(p3); + XYZPoint p4 = new XYZPoint(4, 7); + points.add(p4); + XYZPoint p5 = new XYZPoint(8, 1); + points.add(p5); + XYZPoint p6 = new XYZPoint(7, 2); + points.add(p6); + KdTree kdTree = new KdTree(points); + + Collection result = kdTree.nearestNeighbourSearch(1, p3); + assertTrue("K-D Tree query error. query=(k=1, p=(9, 6)) returned="+result, result.contains(p3)); + + XYZPoint search = new XYZPoint(1, 4); + result = kdTree.nearestNeighbourSearch(4, search); + assertTrue("K-D Tree query error. query=(k=4, p=(1, 4)) returned="+result, (result.contains(p1) && + result.contains(p2) && + result.contains(p4) && + result.contains(p6)) + ); + + kdTree.remove(p6); + kdTree.remove(p4); + kdTree.remove(p3); + kdTree.remove(p5); + kdTree.remove(p1); + kdTree.remove(p2); + } + + @Test + public void testKdTree_as_iterable() { + List points = new ArrayList(); + XYZPoint p1 = new XYZPoint(2, 3); + points.add(p1); + XYZPoint p2 = new XYZPoint(5, 4); + points.add(p2); + XYZPoint p3 = new XYZPoint(9, 6); + points.add(p3); + XYZPoint p4 = new XYZPoint(4, 7); + points.add(p4); + XYZPoint p5 = new XYZPoint(8, 1); + points.add(p5); + XYZPoint p6 = new XYZPoint(7, 2); + points.add(p6); + KdTree kdTree = new KdTree(points); + + for (final XYZPoint p : kdTree) + assertTrue(kdTree.contains(p)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/LCPArrayTest.java b/test/com/jwetherell/algorithms/data_structures/test/LCPArrayTest.java new file mode 100644 index 00000000..4fea9863 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/LCPArrayTest.java @@ -0,0 +1,50 @@ +package com.jwetherell.algorithms.data_structures.test; + +import com.jwetherell.algorithms.data_structures.LCPArray; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; + +import static org.junit.Assert.*; + +public class LCPArrayTest { + + @Test + public void smallTest(){ + String string = "asdasdd"; + LCPArray LCPArrayBuilder = new LCPArray(string); + ArrayList LCPArray = LCPArrayBuilder.getLCPArray(); + ArrayList result = new ArrayList(); + + result.addAll(Arrays.asList(null, 0, 3, 0, 1, 1, 0, 2)); + + assertEquals(LCPArray, result); + } + + @Test + public void longTest(){ + String string = "aasfaasdsadasdfasdasdasdasfdasfassdfas"; + LCPArray LCPArrayBuilder = new LCPArray(string); + ArrayList LCPArray = LCPArrayBuilder.getLCPArray(); + ArrayList result = new ArrayList(); + + result.addAll(Arrays.asList(null, 0, 3, 1, 1, 2, 8, 5, 3, 3, 2, 4, 3, 2, 0, + 6, 4, 3, 4, 1, 4, 1, 0, 2, 3, 3, 1, 0, 1, 1, 7, 4, 2, 5, 2, 1, 3, 2, 1)); + + assertEquals(LCPArray, result); + } + + @Test + public void singleLetterTest(){ + String string = "aaaaaaaaaaaa"; + LCPArray LCPArrayBuilder = new LCPArray(string); + ArrayList LCPArray = LCPArrayBuilder.getLCPArray(); + ArrayList result = new ArrayList(); + + result.addAll(Arrays.asList(null , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); + + assertEquals(LCPArray, result); + } + +} \ No newline at end of file diff --git a/test/com/jwetherell/algorithms/data_structures/test/ListTests.java b/test/com/jwetherell/algorithms/data_structures/test/ListTests.java new file mode 100644 index 00000000..c8806877 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/ListTests.java @@ -0,0 +1,184 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.List; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.ListTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class ListTests { + + @Test + public void testArrayList() { + TestData data = Utils.generateTestData(1000); + + String aName = "List [array]"; + List.ArrayList aList = new List.ArrayList(); + Collection aCollection = aList.toCollection(); + + assertTrue(ListTest.testList(aList, aName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(aCollection, Integer.class, aName, + data.unsorted, data.sorted, data.invalid)); + + // Try some array list specific tests + { + // adding new element at the first spot + for (int i = 0; i < data.unsorted.length; i++) { + Integer item = data.unsorted[i]; + boolean added = aList.add(0,item); + if ((!aList.validate() || (aList.size() != i+1))) { + System.err.println(aName+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,aList); + assertTrue(false); + } + if ((!added || !aList.contains(item))) { + System.err.println(aName+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,aList); + assertTrue(false); + } + } + + boolean contains = aList.contains(data.invalid); + boolean removed = aList.remove(data.invalid); + if (contains || removed) { + System.err.println(aName+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(data.invalid,aList); + assertTrue(false); + } + + int size = aList.size(); + for (int i = 0; i < size; i++) { + Integer item = data.unsorted[i]; + removed = aList.remove(item); + if ((!aList.validate() || (aList.size() != data.unsorted.length-(i+1)))) { + System.err.println(aName+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,aList); + assertTrue(false); + } + if ((!removed || aList.contains(item))) { + System.err.println(aName+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,aList); + assertTrue(false); + } + } + } + { + // adding new element at the middle spot + for (int i = 0; i < data.unsorted.length; i++) { + Integer item = data.unsorted[i]; + int idx = (int) Math.floor(i/2); + boolean added = aList.add(idx,item); + if ((!aList.validate() || (aList.size() != i+1))) { + System.err.println(aName+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,aList); + assertTrue(false); + } + if ((!added || !aList.contains(item))) { + System.err.println(aName+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,aList); + assertTrue(false); + } + } + + boolean contains = aList.contains(data.invalid); + boolean removed = aList.remove(data.invalid); + if (contains || removed) { + System.err.println(aName+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(data.invalid,aList); + assertTrue(false); + } + + int size = aList.size(); + for (int i = 0; i < size; i++) { + Integer item = data.unsorted[i]; + removed = aList.remove(item); + if ((!aList.validate() || (aList.size() != data.unsorted.length-(i+1)))) { + System.err.println(aName+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,aList); + assertTrue(false); + } + if ((!removed || aList.contains(item))) { + System.err.println(aName+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,aList); + assertTrue(false); + } + } + } + { + // adding new element at the end spot + for (int i = 0; i < data.unsorted.length; i++) { + Integer item = data.unsorted[i]; + boolean added = aList.add(i,item); + if ((!aList.validate() || (aList.size() != i+1))) { + System.err.println(aName+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,aList); + assertTrue(false); + } + if ((!added || !aList.contains(item))) { + System.err.println(aName+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,aList); + assertTrue(false); + } + } + + boolean contains = aList.contains(data.invalid); + boolean removed = aList.remove(data.invalid); + if (contains || removed) { + System.err.println(aName+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(data.invalid,aList); + assertTrue(false); + } + + int size = aList.size(); + for (int i = 0; i < size; i++) { + Integer item = data.unsorted[i]; + removed = aList.remove(item); + if ((!aList.validate() || (aList.size() != data.unsorted.length-(i+1)))) { + System.err.println(aName+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,aList); + assertTrue(false); + } + if ((!removed || aList.contains(item))) { + System.err.println(aName+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,aList); + assertTrue(false); + } + } + } + } + + @Test + public void testSinglyLinkedList() { + TestData data = Utils.generateTestData(1000); + + String lName = "List [Singlylinked]"; + List.SinglyLinkedList lList = new List.SinglyLinkedList(); + Collection lCollection = lList.toCollection(); + + assertTrue(ListTest.testList(lList, lName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(lCollection, Integer.class, lName, + data.unsorted, data.sorted, data.invalid)); + } + + @Test + public void testDoublyLinkedList() { + TestData data = Utils.generateTestData(1000); + + String lName = "List [Doublylinked]"; + List.DoublyLinkedList lList = new List.DoublyLinkedList(); + Collection lCollection = lList.toCollection(); + + assertTrue(ListTest.testList(lList, lName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(lCollection, Integer.class, lName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/LowestCommonAncestorTest.java b/test/com/jwetherell/algorithms/data_structures/test/LowestCommonAncestorTest.java new file mode 100644 index 00000000..ae78eabe --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/LowestCommonAncestorTest.java @@ -0,0 +1,56 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.LowestCommonAncestor; +import com.jwetherell.algorithms.data_structures.LowestCommonAncestor.NodesNotInSameTreeException; +import com.jwetherell.algorithms.data_structures.LowestCommonAncestor.TreeNode; + +public class LowestCommonAncestorTest { + + @Test + public void largeTreeTest() throws NodesNotInSameTreeException { + + final TreeNode root = new TreeNode(); + final TreeNode left = root.addChild(); + final TreeNode middle = root.addChild(); + final TreeNode right = root.addChild(); + + //long path + TreeNode v = left; + for (int i = 0; i<1000; i++) + v = v.addChild(); + TreeNode leftRight = left.addChild(); + assertEquals(LowestCommonAncestor.lowestCommonAncestor(v, leftRight), left); + + for (int i = 0; i<2000; i++) { + leftRight = leftRight.addChild(); + assertEquals(LowestCommonAncestor.lowestCommonAncestor(v, leftRight), left); + } + + assertEquals(LowestCommonAncestor.lowestCommonAncestor(middle, right), root); + assertEquals(LowestCommonAncestor.lowestCommonAncestor(root, right), root); + assertEquals(LowestCommonAncestor.lowestCommonAncestor(root, root), root); + + final TreeNode root2 = new TreeNode(); + boolean thrownException = false; + try { + LowestCommonAncestor.lowestCommonAncestor(v, root2); + } catch (NodesNotInSameTreeException e) { + thrownException = true; + } + assertTrue(thrownException); + + final TreeNode deepChild = v.addChild(101); + assertEquals(deepChild, root.find(101)); + assertTrue(root.contains(101)); + + assertNull(root.find(102)); + assertFalse(root.contains(102)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/MatrixTests.java b/test/com/jwetherell/algorithms/data_structures/test/MatrixTests.java new file mode 100644 index 00000000..ca0014bf --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/MatrixTests.java @@ -0,0 +1,114 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assert.assertArrayEquals; +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.Matrix; + +public class MatrixTests { + + @Test + public void testMatrix() { + Matrix matrix1 = new Matrix(4, 3); + matrix1.set(0, 0, 14); + matrix1.set(0, 1, 9); + matrix1.set(0, 2, 3); + matrix1.set(1, 0, 2); + matrix1.set(1, 1, 11); + matrix1.set(1, 2, 15); + matrix1.set(2, 0, 0); + matrix1.set(2, 1, 12); + matrix1.set(2, 2, 17); + matrix1.set(3, 0, 5); + matrix1.set(3, 1, 2); + matrix1.set(3, 2, 3); + + Matrix matrix2 = new Matrix(3, 2); + matrix2.set(0, 0, 12); + matrix2.set(0, 1, 25); + matrix2.set(1, 0, 9); + matrix2.set(1, 1, 10); + matrix2.set(2, 0, 8); + matrix2.set(2, 1, 5); + + // Result of multiplication + Integer[][] array1 = new Integer[][]{{273,455}, + {243,235}, + {244,205}, + {102,160}}; + Matrix result1 = new Matrix(4,2,array1); + + Matrix matrix3 = matrix1.multiply(matrix2); + assertTrue("Matrix multiplication error. matrix3="+matrix3+" result1"+result1, matrix3.equals(result1)); + + int rows = 2; + int cols = 2; + int counter = 0; + Matrix matrix4 = new Matrix(rows, cols); + for (int r = 0; r < rows; r++) + for (int c = 0; c < cols; c++) + matrix4.set(r, c, counter++); + + // Result of subtraction + Integer[][] array2 = new Integer[][]{{0,0}, + {0,0}}; + Matrix result2 = new Matrix(2,2,array2); + + Matrix matrix5 = matrix4.subtract(matrix4); + assertTrue("Matrix subtraction error. matrix5="+matrix5+" result2"+result2, matrix5.equals(result2)); + + // Result of addition + Integer[][] array3 = new Integer[][]{{0,2}, + {4,6}}; + Matrix result3 = new Matrix(2,2,array3); + + Matrix matrix6 = matrix4.add(matrix4); + assertTrue("Matrix addition error. matrix6="+matrix6+" result3"+result3, matrix6.equals(result3)); + + Matrix matrix7 = new Matrix(2, 2); + matrix7.set(0, 0, 1); + matrix7.set(0, 1, 2); + matrix7.set(1, 0, 3); + matrix7.set(1, 1, 4); + + Matrix matrix8 = new Matrix(2, 2); + matrix8.set(0, 0, 1); + matrix8.set(0, 1, 2); + matrix8.set(1, 0, 3); + matrix8.set(1, 1, 4); + + // Result of multiplication + Integer[][] array4 = new Integer[][]{{7,10}, + {15,22}}; + Matrix result4 = new Matrix(2,2,array4); + + Matrix matrix9 = matrix7.multiply(matrix8); + assertTrue("Matrix multiplication error. matrix9="+matrix9+" result4"+result4, matrix9.equals(result4)); + } + + @Test + public void testIdentityMethod1() { + Matrix matrix = new Matrix(2, 2); + matrix.set(0, 0, 0); + matrix.set(0, 1, 0); + matrix.set(1, 0, 0); + matrix.set(1, 1, 0); + + Matrix expectedResult = new Matrix(2, 2); + expectedResult.set(0, 0, 1); + expectedResult.set(0, 1, 0); + expectedResult.set(1, 0, 0); + expectedResult.set(1, 1, 1); + + try{ + matrix = matrix.identity(); + } catch(Exception ex){ + fail(); + } + + assertArrayEquals(expectedResult.getRow(0), matrix.getRow(0)); + assertArrayEquals(expectedResult.getRow(1), matrix.getRow(1)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/PatriciaTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/PatriciaTreeTests.java new file mode 100644 index 00000000..8a053d2b --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/PatriciaTreeTests.java @@ -0,0 +1,30 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.PatriciaTrie; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.TreeTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class PatriciaTreeTests { + + @Test + public void testPatriciaTrie() { + TestData data = Utils.generateTestData(1000); + + String bstName = "PatriciaTrie"; + PatriciaTrie bst = new PatriciaTrie(); + Collection bstCollection = bst.toCollection(); + + assertTrue(TreeTest.testTree(bst, String.class, bstName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(bstCollection, String.class, bstName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/QuadTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/QuadTreeTests.java new file mode 100644 index 00000000..7152f04c --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/QuadTreeTests.java @@ -0,0 +1,129 @@ +package com.jwetherell.algorithms.data_structures.test; + +import java.util.Collections; +import java.util.Random; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.QuadTree; + +public class QuadTreeTests { + + private static final Random RANDOM = new Random(); + private static final int SIZE = 1000; + private static final java.util.Set SET = new java.util.HashSet(SIZE); + private static final java.util.List QUERY = new java.util.ArrayList(SIZE); + + static { + while (SET.size() < SIZE) { + float x = RANDOM.nextInt(SIZE); + float y = RANDOM.nextInt(SIZE); + QuadTree.XYPoint xyPoint = new QuadTree.XYPoint(x,y); + SET.add(xyPoint); + } + + while (QUERY.size() < SIZE) { + float x = RANDOM.nextInt(SIZE); + float y = RANDOM.nextInt(SIZE); + QuadTree.XYPoint xyPoint = new QuadTree.XYPoint(x,y); + QUERY.add(xyPoint); + } + } + + @Test + public void testPointBasedQuadTree() { + QuadTree tree = new QuadTree.PointRegionQuadTree(0,0,SIZE,SIZE); + + // Insert all in set + for (QuadTree.XYPoint p : SET) { + boolean r = tree.insert(p.getX(), p.getY()); + assertTrue("Not added to tree. p="+p.toString(), tree, r); + } + + // We should find all points here (tests structure) + for (QuadTree.XYPoint p : SET) { + java.util.Collection r = tree.queryRange(p.getX(), p.getY(), 1, 1); + assertTrue("Quad tree queryRange error. p="+p.toString()+" r="+r, tree, r.size()>0); + } + + // We may not find all points here (tests query speed) + for (QuadTree.XYPoint p : QUERY) { + java.util.Collection r = tree.queryRange(p.getX(), p.getY(), 1, 1); + if (SET.contains(p)) + assertTrue("Point should be in tree. p="+p.toString()+" r="+r, tree, r.size()>0); + } + + // Result set should not contain duplicates + java.util.List result = new java.util.ArrayList(); + result.addAll(tree.queryRange(0, 0, SIZE, SIZE)); + Collections.sort(result); + QuadTree.XYPoint prev = null; + for (QuadTree.XYPoint p : result) { + assertFalse("Quad tree compare error. p="+p+" prev="+prev+" result="+result, tree, (prev!=null && prev.equals(p))); + prev = p; + } + + // Remove all + for (QuadTree.XYPoint p : SET) { + boolean removed = tree.remove(p.getX(), p.getY()); + assertTrue("Quad tree remove error. removed="+removed+" p="+p.toString(), tree, removed); + } + } + + @Test + public void testRectangleBasedQuadTree() { + QuadTree tree = new QuadTree.MxCifQuadTree(0,0,SIZE,SIZE,10,10); + + // Insert all in set + for (QuadTree.XYPoint p : SET) { + boolean r = tree.insert(p.getX(), p.getY()); + assertTrue("Not added to tree. p="+p.toString(), tree, r); + } + + // We should find all points here (tests structure) + for (QuadTree.XYPoint p : SET) { + java.util.Collection r = tree.queryRange(p.getX(), p.getY(), 1, 1); + assertTrue("Quad tree queryRange error. p="+p.toString()+" r="+r, tree, r.size()>0); + } + + // We may not find all points here (tests query speed) + for (QuadTree.XYPoint p : QUERY) { + java.util.Collection r = tree.queryRange(p.getX(), p.getY(), 1, 1); + if (SET.contains(p)) + assertTrue("Point should be in tree. p="+p.toString()+" r="+r, tree, r.size()>0); + } + + // Result set should not contain duplicates + java.util.ArrayList result = new java.util.ArrayList(); + result.addAll(tree.queryRange(0, 0, SIZE, SIZE)); + Collections.sort(result); + QuadTree.XYPoint prev = null; + for (QuadTree.XYPoint p : result) { + assertFalse("Quad tree compare error. p="+p+" prev="+prev+" result="+result, tree, (prev!=null && prev.equals(p))); + prev = p; + } + + // Remove all + for (QuadTree.XYPoint p : SET) { + boolean removed = tree.remove(p.getX(), p.getY()); + assertTrue("Quad tree remove error. removed="+removed+" p="+p.toString(), tree, removed); + } + } + + // Assertion which won't call toString on the tree unless the assertion fails + private static final

void assertFalse(String msg, QuadTree

obj, boolean isFalse) { + String toString = ""; + if (isFalse==true) + toString = "\n"+obj.toString(); + Assert.assertFalse(msg+toString, isFalse); + } + + // Assertion which won't call toString on the tree unless the assertion fails + private static final

void assertTrue(String msg, QuadTree

obj, boolean isTrue) { + String toString = ""; + if (isTrue==false) + toString = "\n"+obj.toString(); + Assert.assertTrue(msg+toString, isTrue); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/QueueTests.java b/test/com/jwetherell/algorithms/data_structures/test/QueueTests.java new file mode 100644 index 00000000..6f2be148 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/QueueTests.java @@ -0,0 +1,66 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; +import java.util.Iterator; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.Queue; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.QueueTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class QueueTests { + + @Test + public void testArrayQueue() { + TestData data = Utils.generateTestData(2500); + + String aName = "Queue [array]"; + Queue.ArrayQueue aQueue = new Queue.ArrayQueue(); + Collection aCollection = aQueue.toCollection(); + + assertTrue(QueueTest.testQueue(aQueue, aName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(aCollection, Integer.class, aName, + data.unsorted, data.sorted, data.invalid)); + + // Specific test based on bug + aQueue = new Queue.ArrayQueue(); + for (int i = 0; i < 1024; i++) { + aQueue.offer(i); + } + aQueue.poll(); + aQueue.offer(1024); + Iterator it = aQueue.toQueue().iterator(); + while (it.hasNext()) + it.next(); + } + + @Test + public void testLinkedQueue() { + TestData data = Utils.generateTestData(250); + + String lName = "Queue [linked]"; + Queue.LinkedQueue lQueue = new Queue.LinkedQueue(); + Collection lCollection = lQueue.toCollection(); + + assertTrue(QueueTest.testQueue(lQueue, lName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(lCollection, Integer.class, lName, + data.unsorted, data.sorted, data.invalid)); + + lQueue = new Queue.LinkedQueue(); + for (int i = 0; i < 1024; i++) { + lQueue.offer(i); + } + lQueue.poll(); + lQueue.offer(1024); + Iterator it = lQueue.toQueue().iterator(); + while (it.hasNext()) + it.next(); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/RadixTrieTests.java b/test/com/jwetherell/algorithms/data_structures/test/RadixTrieTests.java new file mode 100644 index 00000000..c7b20d03 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/RadixTrieTests.java @@ -0,0 +1,45 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; +import junit.framework.Assert; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.RadixTrie; +import com.jwetherell.algorithms.data_structures.test.common.JavaMapTest; +import com.jwetherell.algorithms.data_structures.test.common.MapTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class RadixTrieTests { + + @Test + public void testRadixTrie() { + TestData data = Utils.generateTestData(1000); + runTests(data); + } + + /** This was an error condition previously in converting a black node with children into a white terminating node **/ + @Test + public void cornerCase() { + RadixTrie map = new RadixTrie(); + map.put("1", 1); + map.put("112", 112); + map.put("113", 1123); + map.put("11", 11); + map.remove("11"); + Integer r = map.put("11", 11); + Assert.assertTrue(r==null); + } + + private void runTests(TestData data) { + String mapName = "RadixTrie"; + RadixTrie map = new RadixTrie(); + java.util.Map jMap = map.toMap(); + + assertTrue(MapTest.testMap(map, String.class, mapName, + data.unsorted, data.invalid)); + assertTrue(JavaMapTest.testJavaMap(jMap, String.class, mapName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/RedBlackTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/RedBlackTreeTests.java new file mode 100644 index 00000000..27fc3a70 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/RedBlackTreeTests.java @@ -0,0 +1,30 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.RedBlackTree; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.TreeTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class RedBlackTreeTests { + + @Test + public void testRedBlackTree() { + TestData data = Utils.generateTestData(1000); + + String bstName = "Red-Black Tree"; + RedBlackTree bst = new RedBlackTree(); + Collection bstCollection = bst.toCollection(); + + assertTrue(TreeTest.testTree(bst, Integer.class, bstName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(bstCollection, Integer.class, bstName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/SegmentTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/SegmentTreeTests.java new file mode 100644 index 00000000..d15d93f8 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/SegmentTreeTests.java @@ -0,0 +1,436 @@ +package com.jwetherell.algorithms.data_structures.test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.SegmentTree; +import com.jwetherell.algorithms.data_structures.SegmentTree.Data; +import com.jwetherell.algorithms.data_structures.SegmentTree.DynamicSegmentTree; +import com.jwetherell.algorithms.data_structures.SegmentTree.FlatSegmentTree; + +public class SegmentTreeTests { + + @Test + public void testQuadrantSegmentTree() { + java.util.List segments = new ArrayList(); + segments.add(new SegmentTree.Data.QuadrantData(0, 1, 0, 0, 0)); // first point in the 0th quadrant + segments.add(new SegmentTree.Data.QuadrantData(1, 0, 1, 0, 0)); // second point in the 1st quadrant + segments.add(new SegmentTree.Data.QuadrantData(2, 0, 0, 1, 0)); // third point in the 2nd quadrant + segments.add(new SegmentTree.Data.QuadrantData(3, 0, 0, 0, 1)); // fourth point in the 3rd quadrant + + // No matter which order the data is given, all tests should pass + + // Initial order. + testQuadrantSegmentTree(segments); + + // Randomize it + Collections.shuffle(segments); + testQuadrantSegmentTree(segments); + + // Try in order + Collections.sort(segments); + testQuadrantSegmentTree(segments); + + // Try reverse order + Collections.sort(segments,REVERSE); + testQuadrantSegmentTree(segments); + } + + private void testQuadrantSegmentTree(java.util.List segments) { // Quadrant Segment tree + FlatSegmentTree tree = new FlatSegmentTree(segments); + + SegmentTree.Data.QuadrantData query = tree.query(0, 3); + assertTrue("Quad tree query error. query=0->3 result="+query, tree, (query.quad0==1 && query.quad1==1 && query.quad2==1 && query.quad3==1)); + + query = tree.query(2, 3); + assertTrue("Quad tree query error. query=2->3 result="+query, tree, (query.quad0==0 && query.quad1==0 && query.quad2==1 && query.quad3==1)); + + query = tree.query(0, 2); + assertTrue("Quad tree query error. query=0->2 result="+query, tree, (query.quad0==1 && query.quad1==1 && query.quad2==1 && query.quad3==0)); + } + + @Test + public void testRangeMaxSegmentTree() { + java.util.List> segments = new ArrayList>(); + segments.add(new SegmentTree.Data.RangeMaximumData(0, (Integer) 4)); + segments.add(new SegmentTree.Data.RangeMaximumData(1, (Integer) 2)); + segments.add(new SegmentTree.Data.RangeMaximumData(2, (Integer) 6)); + segments.add(new SegmentTree.Data.RangeMaximumData(3, (Integer) 3)); + segments.add(new SegmentTree.Data.RangeMaximumData(4, (Integer) 1)); + segments.add(new SegmentTree.Data.RangeMaximumData(5, (Integer) 5)); + segments.add(new SegmentTree.Data.RangeMaximumData(6, (Integer) 0)); + segments.add(new SegmentTree.Data.RangeMaximumData(7, 17, (Integer) 7)); + segments.add(new SegmentTree.Data.RangeMaximumData(21, (Integer) 10)); + + // No matter which order the data is given, all tests should pass + + // Initial order. + testRangeMaxSegmentTree(segments); + + // Randomize it + Collections.shuffle(segments); + testRangeMaxSegmentTree(segments); + + // Try in order + Collections.sort(segments); + testRangeMaxSegmentTree(segments); + + // Try reverse order + Collections.sort(segments,REVERSE); + testRangeMaxSegmentTree(segments); + } + + private void testRangeMaxSegmentTree(java.util.List> segments) { // Range Maximum Segment tree + FlatSegmentTree> tree = new FlatSegmentTree>(segments, 3); + + SegmentTree.Data.RangeMaximumData query = tree.query(0, 7); + assertTrue("Segment tree query error. query=0->7 result="+query, tree, query.maximum==7); + + query = tree.query(0, 21); + assertTrue("Segment tree query error. query=0->21 result="+query, tree, query.maximum==10); + + // bounds checking + { + // max is first + query = tree.query(2, 4); + assertTrue("Segment tree query error. query=2->4 result="+query, tree, query.maximum==6); + + // max is middle + query = tree.query(4, 6); + assertTrue("Segment tree query error. query=4->6 result="+query, tree, query.maximum==5); + + // max is last + query = tree.query(5, 7); + assertTrue("Segment tree query error. query=5->7 result="+query, tree, query.maximum==7); + } + + query = tree.query(7); // stabbing + assertTrue("Segment tree query error. query=7 result="+query, tree, query.maximum==7); + } + + @Test + public void testRangeMinSegmentTree() { + java.util.List> segments = new ArrayList>(); + segments.add(new SegmentTree.Data.RangeMinimumData(0, (Integer) 4)); + segments.add(new SegmentTree.Data.RangeMinimumData(1, (Integer) 2)); + segments.add(new SegmentTree.Data.RangeMinimumData(2, (Integer) 6)); + segments.add(new SegmentTree.Data.RangeMinimumData(3, (Integer) 3)); + segments.add(new SegmentTree.Data.RangeMinimumData(4, (Integer) 1)); + segments.add(new SegmentTree.Data.RangeMinimumData(5, (Integer) 5)); + segments.add(new SegmentTree.Data.RangeMinimumData(6, (Integer) 0)); + segments.add(new SegmentTree.Data.RangeMinimumData(17, (Integer) 7)); + + // No matter which order the data is given, all tests should pass + + // Initial order. + testRangeMinSegmentTree(segments); + + // Randomize it + Collections.shuffle(segments); + testRangeMinSegmentTree(segments); + + // Try in order + Collections.sort(segments); + testRangeMinSegmentTree(segments); + + // Try reverse order + Collections.sort(segments,REVERSE); + testRangeMinSegmentTree(segments); + } + + private void testRangeMinSegmentTree(java.util.List> segments) { // Range Minimum Segment tree + FlatSegmentTree> tree = new FlatSegmentTree>(segments, 5); + + SegmentTree.Data.RangeMinimumData query = tree.query(0, 7); + assertTrue("Segment tree query error. query=0->7 result="+query, tree, query.minimum==0); + + query = tree.query(0, 17); + assertTrue("Segment tree query error. query=0->17 result="+query, tree, query.minimum==0); + + // bounds checking + { + // min is first + query = tree.query(1, 3); + assertTrue("Segment tree query error. query=1->3 result="+query, tree, query.minimum==2); + + // min is middle + query = tree.query(3, 5); + assertTrue("Segment tree query error. query=3->5 result="+query, tree, query.minimum==1); + + // min is last + query = tree.query(1, 4); + assertTrue("Segment tree query error. query=5->7 result="+query, tree, query.minimum==1); + } + + query = tree.query(7); // stabbing + assertTrue("Segment tree query error. query=7 result="+query, tree, query.minimum==null); + } + + @Test + public void testRangeSumSegmentTree() { + java.util.List> segments = new ArrayList>(); + segments.add(new SegmentTree.Data.RangeSumData(0, (Integer) 4)); + segments.add(new SegmentTree.Data.RangeSumData(1, (Integer) 2)); + segments.add(new SegmentTree.Data.RangeSumData(2, (Integer) 6)); + segments.add(new SegmentTree.Data.RangeSumData(3, (Integer) 3)); + segments.add(new SegmentTree.Data.RangeSumData(4, (Integer) 1)); + segments.add(new SegmentTree.Data.RangeSumData(5, (Integer) 5)); + segments.add(new SegmentTree.Data.RangeSumData(6, (Integer) 0)); + segments.add(new SegmentTree.Data.RangeSumData(17, (Integer) 7)); + + // No matter which order the data is given, all tests should pass + + // Initial order. + testRangeSumSegmentTree(segments); + + // Randomize it + Collections.shuffle(segments); + testRangeSumSegmentTree(segments); + + // Try in order + Collections.sort(segments); + testRangeSumSegmentTree(segments); + + // Try reverse order + Collections.sort(segments,REVERSE); + testRangeSumSegmentTree(segments); + } + + private void testRangeSumSegmentTree(java.util.List> segments) { // Range Sum Segment tree + FlatSegmentTree> tree = new FlatSegmentTree>(segments, 10); + + SegmentTree.Data.RangeSumData query = tree.query(0, 8); + assertTrue("Segment tree query error. query=0->8 result="+query, tree, query.sum==21); + + query = tree.query(0, 17); + assertTrue("Segment tree query error. query=0->17 result="+query, tree, query.sum==28); + + query = tree.query(2, 5); + assertTrue("Segment tree query error. query=2->5 result="+query, tree, query.sum==15); + + query = tree.query(10, 17); + assertTrue("Segment tree query error. query=10->17 result="+query, tree, query.sum==7); + + query = tree.query(16); // stabbing + assertTrue("Segment tree query error. query=16 result="+query, tree, query.sum==null); + + query = tree.query(17); // stabbing + assertTrue("Segment tree query error. query=17 result="+query, tree, query.sum==7); + } + + final String stravinsky = "Stravinsky"; + final String schoenberg = "Schoenberg"; + final String grieg = "Grieg"; + final String schubert = "Schubert"; + final String mozart = "Mozart"; + final String schuetz = "Schuetz"; + + @Test + public void testLifespanSegmentTree() { + java.util.List> segments = new ArrayList>(); + segments.add((new SegmentTree.Data.IntervalData(1888, 1971, stravinsky))); + segments.add((new SegmentTree.Data.IntervalData(1874, 1951, schoenberg))); + segments.add((new SegmentTree.Data.IntervalData(1843, 1907, grieg))); + segments.add((new SegmentTree.Data.IntervalData(1779, 1828, schubert))); + segments.add((new SegmentTree.Data.IntervalData(1756, 1791, mozart))); + segments.add((new SegmentTree.Data.IntervalData(1585, 1672, schuetz))); + + // No matter which order the data is given, all tests should pass + + // Initial order. + testLifespanSegmentTree(segments); + + // Randomize it + Collections.shuffle(segments); + testLifespanSegmentTree(segments); + + // Try in order + Collections.sort(segments); + testLifespanSegmentTree(segments); + + // Try reverse order + Collections.sort(segments,REVERSE); + testLifespanSegmentTree(segments); + } + + private void testLifespanSegmentTree(java.util.List> segments) { // Lifespan Interval Segment tree + DynamicSegmentTree> tree = new DynamicSegmentTree>(segments, 25); + + SegmentTree.Data.IntervalData query = tree.query(1890); // Stabbing + assertTrue("Segment tree query error. query=1890 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(stravinsky, schoenberg, grieg))); + + query = tree.query(1909); // Stabbing query + assertTrue("Segment tree query error. query=1909 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(stravinsky, schoenberg))); + + query = tree.query(1585); // Stabbing query + assertTrue("Segment tree query error. query=1585 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(schuetz))); + + query = tree.query(1792, 1903); // Range query + assertTrue("Segment tree query error. query=1792->1903 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(stravinsky, schoenberg, grieg, schubert))); + + query = tree.query(1776, 1799); // Range query + assertTrue("Segment tree query error. query=1776->1799 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(mozart, schubert))); + } + + final String RED = "RED"; + final String ORANGE = "ORANGE"; + final String GREEN = "GREEN"; + final String DARK_GREEN = "DARK_GREEN"; + final String BLUE = "BLUE"; + final String PURPLE = "PURPLE"; + final String BLACK = "BLACK"; + + @Test + public void testIntervalSegmentTree() { + java.util.List> segments = new ArrayList>(); + segments.add((new SegmentTree.Data.IntervalData(2, 6, RED))); + segments.add((new SegmentTree.Data.IntervalData(3, 5, ORANGE))); + segments.add((new SegmentTree.Data.IntervalData(4, 11, GREEN))); + segments.add((new SegmentTree.Data.IntervalData(5, 10, DARK_GREEN))); + segments.add((new SegmentTree.Data.IntervalData(8, 12, BLUE))); + segments.add((new SegmentTree.Data.IntervalData(9, 14, PURPLE))); + segments.add((new SegmentTree.Data.IntervalData(13, 15, BLACK))); + + // No matter which order the data is given, all tests should pass + + // Initial order. + testIntervalSegmentTree(segments); + + // Randomize it + Collections.shuffle(segments); + testIntervalSegmentTree(segments); + + // Try in order + Collections.sort(segments); + testIntervalSegmentTree(segments); + + // Try reverse order + Collections.sort(segments,REVERSE); + testIntervalSegmentTree(segments); + } + + private void testIntervalSegmentTree(java.util.List> segments) { // Interval Segment tree + DynamicSegmentTree> tree = new DynamicSegmentTree>(segments); + + SegmentTree.Data.IntervalData query = tree.query(2); // Stabbing + assertTrue("Segment tree query error. query=2 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(RED))); + + query = tree.query(4); // Stabbing query + assertTrue("Segment tree query error. query=4 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(RED, ORANGE, GREEN))); + + query = tree.query(9); // Stabbing query + assertTrue("Segment tree query error. query=9 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(GREEN, DARK_GREEN, BLUE, PURPLE))); + + query = tree.query(1, 16); // Range query + assertTrue("Segment tree query error. query=1->16 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(RED, ORANGE, GREEN, DARK_GREEN, BLUE, PURPLE, BLACK))); + + query = tree.query(7, 14); // Range query + assertTrue("Segment tree query error. query=7->14 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(GREEN, DARK_GREEN, BLUE, PURPLE, BLACK))); + + query = tree.query(14, 15); // Range query + assertTrue("Segment tree query error. query=14->15 result="+query, tree, collectionsEqual(query.getData(), Arrays.asList(PURPLE, BLACK))); + } + + @Test + public void testIntervalSegmentTree2() { + List> intervals = new ArrayList>(); + intervals.add((new SegmentTree.Data.IntervalData(1, 5, "a"))); + intervals.add((new SegmentTree.Data.IntervalData(2, 6, "b"))); + intervals.add((new SegmentTree.Data.IntervalData(3, 7, "c"))); + intervals.add((new SegmentTree.Data.IntervalData(7, 7, "d"))); + intervals.add((new SegmentTree.Data.IntervalData(8, 8, "e"))); + + DynamicSegmentTree> tree = new DynamicSegmentTree>(intervals); + + SegmentTree.Data.IntervalData query = tree.query(5); // Stabbing query + assertTrue("Segment Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("b","c","a"))); + + query = tree.query(6); // Stabbing query + assertTrue("Segment Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("b","c"))); + + query = tree.query(7); // Stabbing query + assertTrue("Segment Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("c","d"))); + + query = tree.query(1,7); // Range query + assertTrue("Segment Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("a","b","c","d"))); + + query = tree.query(8); // Stabbing query + assertTrue("Segment Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("e"))); + } + + @Test + public void testIntervalSegmentTree3() { + List> intervals = new ArrayList>(); + intervals.add((new SegmentTree.Data.IntervalData(5, 20, "a"))); + intervals.add((new SegmentTree.Data.IntervalData(10, 30, "b"))); + intervals.add((new SegmentTree.Data.IntervalData(12, 15, "c"))); + intervals.add((new SegmentTree.Data.IntervalData(15, 20, "d"))); + intervals.add((new SegmentTree.Data.IntervalData(17, 19, "e"))); + intervals.add((new SegmentTree.Data.IntervalData(30, 40, "f"))); + + DynamicSegmentTree> tree = new DynamicSegmentTree>(intervals); + + SegmentTree.Data.IntervalData query = tree.query(6,7); // Range query + assertTrue("Segment Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("a"))); + } + + @Test + public void testIntervalSegmentTree4() { + List> intervals = new ArrayList>(); + intervals.add((new SegmentTree.Data.IntervalData(15, 20, "a"))); + intervals.add((new SegmentTree.Data.IntervalData(4, 25, "b"))); + intervals.add((new SegmentTree.Data.IntervalData(3, 30, "c"))); + + DynamicSegmentTree> tree = new DynamicSegmentTree>(intervals); + + SegmentTree.Data.IntervalData query = tree.query(26,27); // Range query + assertTrue("Segment Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("c"))); + } + + @Test + public void testIntervalSegmentTree5() { + List> intervals = new ArrayList>(); + intervals.add((new SegmentTree.Data.IntervalData(17, 19, "a"))); + intervals.add((new SegmentTree.Data.IntervalData(5, 11, "b"))); + intervals.add((new SegmentTree.Data.IntervalData(23, 23, "c"))); + intervals.add((new SegmentTree.Data.IntervalData(4, 8, "d"))); + intervals.add((new SegmentTree.Data.IntervalData(15, 18, "e"))); + intervals.add((new SegmentTree.Data.IntervalData(7, 10, "f"))); + + DynamicSegmentTree> tree = new DynamicSegmentTree>(intervals); + + SegmentTree.Data.IntervalData query = tree.query(14,16); // Range query + assertTrue("Segment Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList("e"))); + + query = tree.query(12,14); // Range query + assertTrue("Segment Tree query error. returned=" + query, tree, collectionsEqual(query.getData(), Arrays.asList())); + } + + private static boolean collectionsEqual(Collection c1, Collection c2) { + if (c1.size()!=c2.size()) return false; + return c1.containsAll(c2) && c2.containsAll(c1); + } + + private static final Comparator REVERSE = new Comparator() { + @Override + public int compare(Data arg0, Data arg1) { + int r = arg0.compareTo(arg1); + return r*-1; + } + }; + + // Assertion which won't call toString on the tree unless the assertion fails + private static final void assertTrue(String msg, SegmentTree obj, boolean isTrue) { + String toString = ""; + if (isTrue==false) + toString = "\n"+obj.toString(); + Assert.assertTrue(msg+toString, isTrue); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/SkipListMapTests.java b/test/com/jwetherell/algorithms/data_structures/test/SkipListMapTests.java new file mode 100644 index 00000000..8e7468e5 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/SkipListMapTests.java @@ -0,0 +1,29 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.SkipListMap; +import com.jwetherell.algorithms.data_structures.test.common.JavaMapTest; +import com.jwetherell.algorithms.data_structures.test.common.MapTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class SkipListMapTests { + + @Test + public void testSkipListMap() { + TestData data = Utils.generateTestData(1000); + + String mapName = "SkipListMap"; + SkipListMap map = new SkipListMap(); + java.util.Map jMap = map.toMap(); + + assertTrue(MapTest.testMap(map, String.class, mapName, + data.unsorted, data.invalid)); + assertTrue(JavaMapTest.testJavaMap(jMap, Integer.class, mapName, + data.unsorted, data.sorted, data.invalid)); + } + +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/SkipListTests.java b/test/com/jwetherell/algorithms/data_structures/test/SkipListTests.java new file mode 100644 index 00000000..58da6a5d --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/SkipListTests.java @@ -0,0 +1,30 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.SkipList; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.SetTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class SkipListTests { + + @Test + public void testSkipList() { + TestData data = Utils.generateTestData(1000); + + String sName = "SkipList"; + SkipList sList = new SkipList(); + Collection lCollection = sList.toCollection(); + + assertTrue(SetTest.testSet(sList, sName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(lCollection, Integer.class, sName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/SplayTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/SplayTreeTests.java new file mode 100644 index 00000000..fb1e9781 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/SplayTreeTests.java @@ -0,0 +1,32 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.BinarySearchTree; +import com.jwetherell.algorithms.data_structures.SplayTree; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.TreeTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class SplayTreeTests { + + @Test + public void testSplayTree() { + TestData data = Utils.generateTestData(1000); + + String bstName = "Splay Tree"; + BinarySearchTree bst = new SplayTree(); + Collection bstCollection = bst.toCollection(); + + assertTrue(TreeTest.testTree(bst, Integer.class, bstName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(bstCollection, Integer.class, bstName, + data.unsorted, data.sorted, data.invalid)); + } + +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/StackTests.java b/test/com/jwetherell/algorithms/data_structures/test/StackTests.java new file mode 100644 index 00000000..c86d76ae --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/StackTests.java @@ -0,0 +1,44 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.Stack; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.StackTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class StackTests { + + @Test + public void testArrayStack() { + TestData data = Utils.generateTestData(1000); + + String aName = "Stack [array]"; + Stack.ArrayStack aStack = new Stack.ArrayStack(); + Collection aCollection = aStack.toCollection(); + + assertTrue(StackTest.testStack(aStack, aName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(aCollection, Integer.class, aName, + data.unsorted, data.sorted, data.invalid)); + } + + @Test + public void testLinkedStack() { + TestData data = Utils.generateTestData(1000); + + String lName = "Stack [linked]"; + Stack.LinkedStack lStack = new Stack.LinkedStack(); + Collection lCollection = lStack.toCollection(); + + assertTrue(StackTest.testStack(lStack, lName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(lCollection, Integer.class, lName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/SuffixArrayTest.java b/test/com/jwetherell/algorithms/data_structures/test/SuffixArrayTest.java new file mode 100644 index 00000000..18002fbc --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/SuffixArrayTest.java @@ -0,0 +1,44 @@ +package com.jwetherell.algorithms.data_structures.test; + +import com.jwetherell.algorithms.data_structures.SuffixArray; +import com.jwetherell.algorithms.data_structures.SuffixTree; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Set; + +import static org.junit.Assert.*; + +public class SuffixArrayTest { + + @Test + public void testSuffixArray(){ + String string = "aasfaasdsadasdfasdasdasdasfdasfassdfas"; + + SuffixArray suffixArrayBuilder = new SuffixArray(string); + SuffixTree suffixTree = new SuffixTree(string); + + Set suffixSet = suffixTree.getSuffixes(); + ArrayList suffixArray = suffixArrayBuilder.getSuffixArray(); + + int i=0; + for(String suffix : suffixSet){ + String substring = string.substring(suffixArray.get(i++)); + assertTrue(suffix.equals(substring)); + } + } + + @Test + public void testKMRarray(){ + String string = "aasfaasdsadasdfasdasdasdasfdasfassdfas"; + + SuffixArray suffixArrayBuilder = new SuffixArray(string); + ArrayList suffixArray = suffixArrayBuilder.getSuffixArray(); + ArrayList KMRarray = suffixArrayBuilder.getKMRarray(); + + int length = string.length(); + for(int i=0; i tree = new SuffixTree(bookkeeper); + assertTrue(SuffixTreeTest.suffixTreeTest(tree, bookkeeper)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/SuffixTrieTests.java b/test/com/jwetherell/algorithms/data_structures/test/SuffixTrieTests.java new file mode 100644 index 00000000..0da3e6fb --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/SuffixTrieTests.java @@ -0,0 +1,18 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.SuffixTrie; +import com.jwetherell.algorithms.data_structures.test.common.SuffixTreeTest; + +public class SuffixTrieTests { + + @Test + public void testSuffixTrie() { + String bookkeeper = "bookkeeper"; + SuffixTrie trie = new SuffixTrie(bookkeeper); + assertTrue(SuffixTreeTest.suffixTreeTest(trie, bookkeeper)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/TernarySearchTreeTests.java b/test/com/jwetherell/algorithms/data_structures/test/TernarySearchTreeTests.java new file mode 100644 index 00000000..82a5b70a --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/TernarySearchTreeTests.java @@ -0,0 +1,77 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.TernarySearchTree; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.TreeTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class TernarySearchTreeTests { + + @Test + public void test1() { + final List tests = new ArrayList(); + tests.add("BEE"); + tests.add("BEEN"); + tests.add("BEAST"); + tests.add("BELOW"); + tests.add("BEFORE"); + tests.add("BUT"); + tests.add("CAT"); + tests.add("BE"); + tests.add("B"); + tests.add("DAD"); + tests.add("APPLE"); + + final TernarySearchTree bst = new TernarySearchTree(); + + // Add + bst.add(null); + bst.add(""); + for (String s : tests) + bst.add(s); + bst.add(null); + bst.add(""); + Assert.assertFalse(bst.add("BE")); + + // contains + Assert.assertFalse(bst.contains(null)); + Assert.assertFalse(bst.contains("")); + for (String s : tests) + Assert.assertTrue(bst.contains(s)); + Assert.assertFalse(bst.contains(null)); + Assert.assertFalse(bst.contains("")); + + // remove + Assert.assertTrue(bst.remove(null)==null); + Assert.assertTrue(bst.remove("")==null); + for (String s : tests) + Assert.assertTrue(bst.remove(s)!=null); + Assert.assertTrue(bst.remove(null)==null); + Assert.assertTrue(bst.remove("")==null); + } + + @Test + public void testTernary() { + TestData data = Utils.generateTestData(1000); + + String bstName = "TernarySearchTreeTests"; + TernarySearchTree bst = new TernarySearchTree(); + Collection bstCollection = bst.toCollection(); + + assertTrue(TreeTest.testTree(bst, String.class, bstName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(bstCollection, String.class, bstName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/TreapTests.java b/test/com/jwetherell/algorithms/data_structures/test/TreapTests.java new file mode 100644 index 00000000..074ed834 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/TreapTests.java @@ -0,0 +1,31 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.BinarySearchTree; +import com.jwetherell.algorithms.data_structures.Treap; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.TreeTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class TreapTests { + + @Test + public void testTreap() { + TestData data = Utils.generateTestData(1000); + + String bstName = "Treap"; + BinarySearchTree bst = new Treap(); + Collection bstCollection = bst.toCollection(); + + assertTrue(TreeTest.testTree(bst, Integer.class, bstName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(bstCollection, Integer.class, bstName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/TreeMapTests.java b/test/com/jwetherell/algorithms/data_structures/test/TreeMapTests.java new file mode 100644 index 00000000..83490c60 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/TreeMapTests.java @@ -0,0 +1,28 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.TreeMap; +import com.jwetherell.algorithms.data_structures.test.common.JavaMapTest; +import com.jwetherell.algorithms.data_structures.test.common.MapTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class TreeMapTests { + + @Test + public void testTreeMap() { + TestData data = Utils.generateTestData(1000); + + String mapName = "TreeMap"; + TreeMap map = new TreeMap(); + java.util.Map jMap = map.toMap(); + + assertTrue(MapTest.testMap(map, String.class, mapName, + data.unsorted, data.invalid)); + assertTrue(JavaMapTest.testJavaMap(jMap, Integer.class, mapName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/TrieMapTests.java b/test/com/jwetherell/algorithms/data_structures/test/TrieMapTests.java new file mode 100644 index 00000000..8a159b85 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/TrieMapTests.java @@ -0,0 +1,28 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.TrieMap; +import com.jwetherell.algorithms.data_structures.test.common.JavaMapTest; +import com.jwetherell.algorithms.data_structures.test.common.MapTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class TrieMapTests { + + @Test + public void testTrieMap() { + TestData data = Utils.generateTestData(1000); + + String mapName = "TrieMap"; + TrieMap map = new TrieMap(); + java.util.Map jMap = map.toMap(); + + assertTrue(MapTest.testMap(map, String.class, mapName, + data.unsorted, data.invalid)); + assertTrue(JavaMapTest.testJavaMap(jMap, String.class, mapName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/TrieTests.java b/test/com/jwetherell/algorithms/data_structures/test/TrieTests.java new file mode 100644 index 00000000..f2358174 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/TrieTests.java @@ -0,0 +1,30 @@ +package com.jwetherell.algorithms.data_structures.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.Trie; +import com.jwetherell.algorithms.data_structures.test.common.JavaCollectionTest; +import com.jwetherell.algorithms.data_structures.test.common.TreeTest; +import com.jwetherell.algorithms.data_structures.test.common.Utils; +import com.jwetherell.algorithms.data_structures.test.common.Utils.TestData; + +public class TrieTests { + + @Test + public void testTrie() { + TestData data = Utils.generateTestData(1000); + + String bstName = "Trie"; + Trie bst = new Trie(); + Collection bstCollection = bst.toCollection(); + + assertTrue(TreeTest.testTree(bst, String.class, bstName, + data.unsorted, data.invalid)); + assertTrue(JavaCollectionTest.testCollection(bstCollection, String.class, bstName, + data.unsorted, data.sorted, data.invalid)); + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/HeapTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/HeapTest.java new file mode 100644 index 00000000..b580f666 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/HeapTest.java @@ -0,0 +1,168 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import java.util.Arrays; + +import com.jwetherell.algorithms.data_structures.BinaryHeap; +import com.jwetherell.algorithms.data_structures.interfaces.IHeap; + +public class HeapTest { + + public static > boolean testHeap(BinaryHeap.Type heapType, IHeap heap, Class type, String name, + Integer[] unsorted, Integer[] sorted, Integer _invalid) { + for (int i = 0; i < unsorted.length; i++) { + T item = Utils.parseT(unsorted[i], type); + boolean added = heap.add(item); + if (!heap.validate() || (heap.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(sorted,heap); + return false; + } + if (!added || !heap.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(sorted,heap); + return false; + } + } + + T invalid = Utils.parseT(_invalid, type); + boolean contains = heap.contains(invalid); + T removed = heap.remove(invalid); + if (contains || (removed!=null)) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,heap); + return false; + } + + int size = heap.size(); + for (int i = 0; i < size; i++) { + T item = heap.removeHead(); + Integer _correct = ((heapType == BinaryHeap.Type.MIN)?sorted[i]:sorted[sorted.length-(i+1)]); + T correct = Utils.parseT(_correct, type); + if (item.compareTo(correct)!=0) { + System.err.println(name+" YIKES!! " + item + " does not match heap item."); + Utils.handleError(unsorted,heap); + return false; + } + if (!heap.validate() || (heap.size() != unsorted.length-(i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(unsorted,heap); + return false; + } + if (heap.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(unsorted,heap); + return false; + } + } + + // Add half, remove a quarter, add three-quarters, remove all + int quarter = unsorted.length/4; + int half = unsorted.length/2; + Integer[] halfArray = Arrays.copyOf(unsorted, half); + Arrays.sort(halfArray); + Integer[] quarterArray = new Integer[quarter]; + Integer[] sortedQuarterArray = new Integer[quarter]; //Needed for binary search + for (int i=0; i=0) { + threeQuartersArray[idx++] = i; + } else { + index = Arrays.binarySearch(halfArray, i); + if (index<0) threeQuartersArray[idx++] = i; + } + } else { + if (index>=0) { + threeQuartersArray[idx++] = i; + } else { + index = Arrays.binarySearch(halfArray, i); + if (index<0) threeQuartersArray[idx++] = i; + } + } + } + for (int i = 0; i < half; i++) { + T item = Utils.parseT(unsorted[i], type); + boolean added = heap.add(item); + if (!heap.validate() || (heap.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(unsorted,heap); + return false; + } + if (!added || !heap.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(unsorted,heap); + return false; + } + } + for (int i = 0; i < quarter; i++) { + T item = heap.removeHead(); + Integer _correct = quarterArray[i]; + T correct = Utils.parseT(_correct, type); + if (item.compareTo(correct)!=0) { + System.err.println(name+" YIKES!! " + item + " does not match heap item."); + Utils.handleError(unsorted,heap); + return false; + } + if (!heap.validate() || (heap.size() != half-(i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(unsorted,heap); + return false; + } + if (heap.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(unsorted,heap); + return false; + } + } + for (int i = 0; i < threeQuartersArray.length; i++) { + Integer _item = threeQuartersArray[i]; + T item = Utils.parseT(_item, type); + boolean added = heap.add(item); + if (!heap.validate() || (heap.size() != (half-quarter)+(i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(unsorted,heap); + return false; + } + if (!added || !heap.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(unsorted,heap); + return false; + } + } + + for (int i = 0; i < sorted.length; i++) { + T item = heap.removeHead(); + Integer _correct = ((heapType == BinaryHeap.Type.MIN)?sorted[i]:sorted[sorted.length-(i+1)]); + T correct = Utils.parseT(_correct, type); + if (item.compareTo(correct)!=0) { + System.err.println(name+" YIKES!! " + item + " does not match heap item."); + Utils.handleError(sorted,heap); + return false; + } + if (!heap.validate() || (heap.size() != sorted.length-(i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(sorted,heap); + return false; + } + if (heap.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(sorted,heap); + return false; + } + } + if (heap.size() != 0) { + System.err.println(name+" YIKES!! a size mismatch."); + Utils.handleError(sorted,heap); + return false; + } + + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/IteratorTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/IteratorTest.java new file mode 100644 index 00000000..fd30f061 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/IteratorTest.java @@ -0,0 +1,17 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import java.util.Iterator; + +public class IteratorTest { + + public static > boolean testIterator(Iterator iter) { + while (iter.hasNext()) { + T item = iter.next(); + if (item==null) { + System.err.println("Iterator failure."); + return false; + } + } + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/JavaCollectionTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/JavaCollectionTest.java new file mode 100644 index 00000000..c4338d5a --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/JavaCollectionTest.java @@ -0,0 +1,262 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import java.util.Collection; + +public class JavaCollectionTest { + + public static > boolean testCollection(Collection collection, Class type, String name, + Integer[] unsorted, Integer[] sorted, Integer _invalid) { + // Make sure the collection is empty + if (!collection.isEmpty()) { + System.err.println(name+" initial isEmpty() failed."); + Utils.handleError(collection); + return false; + } + + if (collection.size()!=0) { + System.err.println(name+" initial size() failed."); + Utils.handleError(collection); + return false; + } + + addAndRemoveInOrder(collection, type, name, unsorted, _invalid); + addInOrderRemoveInReverseOrder(collection, type, name, unsorted, _invalid); + addInReverseOrderAndRemoveInOrder(collection, type, name, unsorted, _invalid); + + addAndRemoveInOrder(collection, type, name, sorted, _invalid); + addInOrderRemoveInReverseOrder(collection, type, name, sorted, _invalid); + addInReverseOrderAndRemoveInOrder(collection, type, name, sorted, _invalid); + + // Make sure the collection is empty + if (!collection.isEmpty()) { + System.err.println(name+" initial isEmpty() failed."); + Utils.handleError(collection); + return false; + } + + if (collection.size()!=0) { + System.err.println(name+" initial size() failed."); + Utils.handleError(collection); + return false; + } + + return true; + } + + private static > boolean addAndRemoveInOrder(Collection collection, Class type, String name, + Integer[] data, Integer _invalid) { + T invalid = Utils.parseT(_invalid, type); + + // Add and remove in order (from index zero to length) + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + boolean added = collection.add(item); + if (!added) { + System.err.println(name+" addAndRemoveInOrder add failed."); + Utils.handleError(data,collection); + return false; + } + } + + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + boolean contains = collection.contains(item); + if (!contains) { + System.err.println(name+" addAndRemoveInOrder contains failed."); + Utils.handleError(data,collection); + return false; + } + } + + boolean contains = collection.contains((T)invalid); + boolean removed = collection.remove((T)invalid); + if (contains || removed) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,collection); + return false; + } + + if (!IteratorTest.testIterator(collection.iterator())) { + System.err.println(name+" addAndRemoveInOrder iterator failed."); + Utils.handleError(data,collection); + return false; + } + + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + removed = collection.remove(item); + if (!removed) { + System.err.println(name+" addAndRemoveInOrder remove failed."); + Utils.handleError(data,collection); + return false; + } + } + + if (!collection.isEmpty()) { + System.err.println(name+" addAndRemoveInOrder isEmpty() failed."); + Utils.handleError(data,collection); + return false; + } + if (collection.size()!=0) { + System.err.println(name+" addAndRemoveInOrder size() failed."); + Utils.handleError(data,collection); + return false; + } + + if (collection instanceof java.util.List && + (!ListIteratorTest.testListIterator(((java.util.List)collection).listIterator(), type, + data, data.length)) + ) { + System.err.println(name+" addAndRemoveInOrder list iterator failed."); + Utils.handleError(data,collection); + return false; + } + + return true; + } + + private static > boolean addInReverseOrderAndRemoveInOrder(Collection collection, Class type, String name, + Integer[] data, Integer _invalid) { + T invalid = Utils.parseT(_invalid, type); + + // Add in reverse (from index length-1 to zero) order and then remove in order (from index zero to length) + for (int i = data.length - 1; i >= 0; i--) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + boolean added = collection.add(item); + if (!added) { + System.err.println(name+" addInReverseOrderAndRemoveInOrder add failed."); + Utils.handleError(data,collection); + return false; + } + } + + boolean contains = collection.contains((T)invalid); + boolean removed = collection.remove((T)invalid); + if (contains || removed) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,collection); + return false; + } + + if (!IteratorTest.testIterator(collection.iterator())) { + System.err.println(name+" addInReverseOrderAndRemoveInOrder iterator failed."); + Utils.handleError(data,collection); + return false; + } + + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + contains = collection.contains(item); + if (!contains) { + System.err.println(name+" addInReverseOrderAndRemoveInOrder contains failed."); + Utils.handleError(data,collection); + return false; + } + } + + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + removed = collection.remove(item); + if (!removed) { + System.err.println(name+" addInReverseOrderAndRemoveInOrder remove failed."); + Utils.handleError(data,collection); + return false; + } + } + + if (!collection.isEmpty()) { + System.err.println(name+" addInReverseOrderAndRemoveInOrder isEmpty() failed."); + Utils.handleError(data,collection); + return false; + } + + if (collection.size()!=0) { + System.err.println(name+" addInReverseOrderAndRemoveInOrder size() failed."); + Utils.handleError(data,collection); + return false; + } + + return true; + } + + public static > boolean addInOrderRemoveInReverseOrder(Collection collection, Class type, String name, + Integer[] data, Integer _invalid) { + T invalid = Utils.parseT(_invalid, type); + + // Add in order (from index zero to length) and then remove in reverse (from index length-1 to zero) order + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + boolean added = collection.add(item); + if (!added) { + System.err.println(name+" addInOrderRemoveInReverseOrder add failed."); + Utils.handleError(data,collection); + return false; + } + } + + boolean contains = collection.contains((T)invalid); + boolean removed = collection.remove((T)invalid); + if (contains || removed) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,collection); + return false; + } + + if (!IteratorTest.testIterator(collection.iterator())) { + System.err.println(name+" addInOrderRemoveInReverseOrder iterator failed."); + Utils.handleError(data,collection); + return false; + } + + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + contains = collection.contains(item); + if (!contains) { + System.err.println(name+" addInOrderRemoveInReverseOrder contains failed."); + Utils.handleError(data,collection); + return false; + } + } + + for (int i = data.length - 1; i >= 0; i--) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + removed = collection.remove(item); + if (!removed) { + System.err.println(name+" addInOrderRemoveInReverseOrder remove failed."); + Utils.handleError(data,collection); + return false; + } + } + + if (!collection.isEmpty()) { + System.err.println(name+" addInOrderRemoveInReverseOrder isEmpty() failed."); + Utils.handleError(data,collection); + return false; + } + if (collection.size()!=0) { + System.err.println(name+" addInOrderRemoveInReverseOrder size() failed."); + Utils.handleError(data,collection); + return false; + } + + if (collection instanceof java.util.List && + (!ListIteratorTest.testListIterator(((java.util.List)collection).listIterator(), type, + data, data.length)) + ) { + System.err.println(name+" addInOrderRemoveInReverseOrder list iterator failed."); + Utils.handleError(data,collection); + return false; + } + + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/JavaMapTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/JavaMapTest.java new file mode 100644 index 00000000..df74fbdd --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/JavaMapTest.java @@ -0,0 +1,314 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +@SuppressWarnings("unchecked") +public class JavaMapTest { + + public static > boolean testJavaMap(java.util.Map map, Class type, String name, + Integer[] unsorted, Integer[] sorted, Integer _invalid) { + // Make sure the map is empty + if (!map.isEmpty()) { + System.err.println(name+" initial isEmpty() failed."); + Utils.handleError(map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" initial size() failed."); + Utils.handleError(map); + return false; + } + + addInOrderAndRemoveInOrder(map, type, name, + unsorted, _invalid); + addInReverseOrderAndRemoveInReverseOrder(map, type, name, + unsorted, _invalid); + addInOrderAndRemoveInReverseOrder(map, type, name, + unsorted, _invalid); + + addInOrderAndRemoveInOrder(map, type, name, sorted, _invalid); + addInReverseOrderAndRemoveInReverseOrder(map, type, name, + sorted, _invalid); + addInOrderAndRemoveInReverseOrder(map, type, name, + sorted, _invalid); + + // Make sure the map is empty + if (!map.isEmpty()) { + System.err.println(name+" initial isEmpty() failed."); + Utils.handleError(map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" initial size() failed."); + Utils.handleError(map); + return false; + } + + return true; + } + + private static > boolean addInOrderAndRemoveInOrder(java.util.Map map, Class keyType, String name, + Integer[] data, Integer _invalid) + { + for (int i = 0; i < data.length; i++) { + Integer item = data[i]; + K k = null; + V v = null; + if (keyType.isAssignableFrom(Integer.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, String.class); + } else if (keyType.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, Integer.class); + } + map.put(k, v); + } + + K invalidKey = (K) Utils.parseT(_invalid, keyType); + boolean contains = map.containsKey(invalidKey); + V removed = map.remove(invalidKey); + if (contains || (removed!=null)) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,map); + return false; + } + + for (Integer item : data) { + K k = (K)Utils.parseT(item, keyType); + map.containsKey(k); + } + + for (int i = 0; i < data.length; i++) { + Integer item = data[i]; + K k = (K)Utils.parseT(item, keyType); + removed = map.remove(k); + if (removed==null) { + System.err.println(name+" invalidity check. removed=" + removed); + Utils.handleError(data,map); + return false; + } + + } + + if (!testMapEntrySet(map, keyType, data)) return false; + + if (!map.isEmpty()) { + System.err.println(name+" isEmpty() failed."); + Utils.handleError(data,map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" size() failed."); + Utils.handleError(data,map); + return false; + } + return true; + } + + private static > boolean addInReverseOrderAndRemoveInReverseOrder(java.util.Map map, Class keyType, String name, + Integer[] data, Integer _invalid) + { + for (int i = data.length - 1; i >= 0; i--) { + Integer item = data[i]; + K k = null; + V v = null; + if (keyType.isAssignableFrom(Integer.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, String.class); + } else if (keyType.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, String.class); + } + map.put(k, v); + } + + K invalidKey = (K)Utils.parseT(_invalid, keyType); + boolean contains = map.containsKey(invalidKey); + V removed = map.remove(invalidKey); + if (contains || (removed!=null)) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,map); + return false; + } + + for (Integer item : data) { + K k = (K)Utils.parseT(item, keyType); + map.containsKey(k); + } + + for (int i = data.length - 1; i >= 0; i--) { + Integer item = data[i]; + K k = (K)Utils.parseT(item, keyType); + removed = map.remove(k); + if (removed==null) { + System.err.println(name+" invalidity check. removed=" + removed); + Utils.handleError(data,map); + return false; + } + } + + if (!map.isEmpty()) { + System.err.println(name+" isEmpty() failed."); + Utils.handleError(data,map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" size() failed."); + Utils.handleError(data,map); + return false; + } + return true; + } + + private static > boolean addInOrderAndRemoveInReverseOrder(java.util.Map map, Class keyType, String name, + Integer[] data, Integer _invalid) { + for (int i = 0; i < data.length; i++) { + Integer item = data[i]; + K k = null; + V v = null; + if (keyType.isAssignableFrom(Integer.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, String.class); + } else if (keyType.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, Integer.class); + } + map.put(k, v); + } + + K invalidKey = (K)Utils.parseT(_invalid, keyType); + boolean contains = map.containsKey(invalidKey); + V removed = map.remove(invalidKey); + if (contains || (removed!=null)) { + System.err.println(name+" sorted invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,map); + return false; + } + + for (Integer item : data) { + K k = (K)Utils.parseT(item, keyType); + map.containsKey(k); + } + + for (int i = data.length - 1; i >= 0; i--) { + Integer item = data[i]; + K k = (K)Utils.parseT(item, keyType); + removed = map.remove(k); + if (removed==null) { + System.err.println(name+" invalidity check. removed=" + removed); + Utils.handleError(data,map); + return false; + } + } + + if (!testMapEntrySet(map, keyType, data)) return false; + + if (!map.isEmpty()) { + System.err.println(name+" sorted isEmpty() failed."); + Utils.handleError(data,map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" sorted size() failed."); + Utils.handleError(data,map); + return false; + } + return true; + } + + private static > boolean testMapEntrySet(java.util.Map map, Class keyType, + Integer[] data) { + { // Test keys + for (int i = 0; i < data.length; i++) { + Integer item = data[i]; + K k = null; + V v = null; + if (keyType.isAssignableFrom(Integer.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, String.class); + } else if (keyType.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, Integer.class); + } + map.put(k, v); + } + + java.util.Set set = map.keySet(); + for (int i = 0; i < data.length; i++) { + Integer item = data[i]; + K k = (K)Utils.parseT(item, keyType); + if (!set.contains(k)) { + System.err.println("MayEntry contains() failure."); + Utils.handleError(data,map); + return false; + } + } + + java.util.Iterator keyIter = set.iterator(); + while (keyIter.hasNext()) { + keyIter.next(); + keyIter.remove(); + } + + if (!map.isEmpty()) { + System.err.println("MayEntry isEmpty() failure."); + Utils.handleError(data,map); + return false; + } + if (map.size()!=0) { + System.err.println("MayEntry size()!=0 failure."); + Utils.handleError(data,map); + return false; + } + } + + { // Test values + for (int i = 0; i < data.length; i++) { + Integer item = data[i]; + K k = null; + V v = null; + if (keyType.isAssignableFrom(Integer.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, String.class); + } else if (keyType.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, keyType); + v = (V)Utils.parseT(item, Integer.class); + } + map.put(k, v); + } + + java.util.Collection collection = map.values(); + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + V v = null; + // These are reversed on purpose + if (keyType.isAssignableFrom(Integer.class)) { + v = (V)Utils.parseT(value, String.class); + } else if (keyType.isAssignableFrom(String.class)) { + v = (V)Utils.parseT(value, Integer.class); + } + if (!collection.contains(v)) { + System.err.println("MayEntry contains() failure."); + Utils.handleError(data,map); + return false; + } + } + + java.util.Iterator valueIter = collection.iterator(); + while (valueIter.hasNext()) { + valueIter.next(); + valueIter.remove(); + } + + if (!map.isEmpty()) { + System.err.println("MayEntry isEmpty() failure."); + Utils.handleError(data,map); + return false; + } + if (map.size()!=0) { + System.err.println("MayEntry size()!=0 failure."); + Utils.handleError(data,map); + return false; + } + } + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/ListIteratorTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/ListIteratorTest.java new file mode 100644 index 00000000..0d2eb1a1 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/ListIteratorTest.java @@ -0,0 +1,106 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import java.util.ListIterator; +import java.util.NoSuchElementException; + +public class ListIteratorTest { + + public static > boolean testListIterator(ListIterator iter, Class type, + Integer[] data, int size) { + // Make sure you catch going prev at the start + boolean exceptionThrown = false; + try { + iter.previous(); + } catch (NoSuchElementException e) { + exceptionThrown = true; + } + if (!exceptionThrown) { + System.err.println("ListIterator exception failure."); + return false; + } + + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + iter.add(item); + } + while (iter.hasPrevious()) + iter.previous(); + + int i = 0; + while (iter.hasNext()) { + T item = iter.next(); + int idx = iter.nextIndex(); + if (idx!=++i) { + System.err.println("ListIterator index failure."); + return false; + } + if (item==null) { + System.err.println("ListIterator item is null."); + return false; + } + } + + // We should be at the end of the collection, this should fail + exceptionThrown = false; + try { + iter.next(); + } catch (NoSuchElementException e) { + exceptionThrown = true; + } + if (!exceptionThrown) { + System.err.println("ListIterator exception failure."); + return false; + } + + //This should be list.size + iter.nextIndex(); + int listSize = iter.nextIndex(); + if (listSize!=size) { + System.err.println("ListIterator ARRAY_SIZE failure."); + return false; + } + + i--; + while (iter.hasPrevious()) { + T item = iter.previous(); + int idx = iter.previousIndex(); + if (idx!=--i) { + System.err.println("ListIterator index failure."); + return false; + } + if (item==null) { + System.err.println("ListIterator item is null."); + return false; + } + } + + // We should be at the beginning of the collection, this should fail + exceptionThrown = false; + try { + iter.previous(); + } catch (NoSuchElementException e) { + exceptionThrown = true; + } + if (!exceptionThrown) { + System.err.println("ListIterator exception failure."); + return false; + } + + // This should be negative one + iter.previousIndex(); + int negOne = iter.previousIndex(); + if (negOne!=-1) { + System.err.println("ListIterator negative_one failure."); + return false; + } + + // Remove all using iterator + while (iter.hasNext()) { + iter.next(); + iter.remove(); + } + + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/ListTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/ListTest.java new file mode 100644 index 00000000..67b5914d --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/ListTest.java @@ -0,0 +1,116 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import com.jwetherell.algorithms.data_structures.interfaces.IList; + +public class ListTest { + + public static > boolean testList(IList list, String name, + T[] data, T _invalid) { + for (int i = 0; i < data.length; i++) { + T item = data[i]; + boolean added = list.add(item); + if ((!list.validate() || (list.size() != i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,list); + return false; + } + if ((!added || !list.contains(item))) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,list); + return false; + } + } + + boolean contains = list.contains(_invalid); + boolean removed = list.remove(_invalid); + if (contains || removed) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,list); + return false; + } + + int size = list.size(); + for (int i = 0; i < size; i++) { + T item = data[i]; + removed = list.remove(item); + if ((!list.validate() || (list.size() != data.length-(i+1)))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,list); + return false; + } + if ((!removed || list.contains(item))) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,list); + return false; + } + } + + // Add half, remove a quarter, add three-quarters, remove all + int quarter = data.length/4; + int half = data.length/2; + for (int i = 0; i < half; i++) { + T item = data[i]; + boolean added = list.add(item); + if ((!list.validate() || (list.size() != i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,list); + return false; + } + if ((!added || !list.contains(item))) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,list); + return false; + } + } + for (int i = (half-1); i >= quarter; i--) { + T item = data[i]; + removed = list.remove(item); + if ((!list.validate() || (list.size() != i))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,list); + return false; + } + if ((!removed || list.contains(item))) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,list); + return false; + } + } + for (int i = quarter; i < data.length; i++) { + T item = data[i]; + boolean added = list.add(item); + if ((!list.validate() || (list.size() != i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,list); + return false; + } + if ((!added || !list.contains(item))) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,list); + return false; + } + } + for (int i = data.length-1; i >= 0; i--) { + T item = data[i]; + removed = list.remove(item); + if ((!list.validate() || (list.size() != i))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,list); + return false; + } + if ((!removed || list.contains(item))) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,list); + return false; + } + } + + if ((list.size() != 0)) { + System.err.println(name+" YIKES!! a size mismatch."); + Utils.handleError(data,list); + return false; + } + + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/MapTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/MapTest.java new file mode 100644 index 00000000..41edd578 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/MapTest.java @@ -0,0 +1,167 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import com.jwetherell.algorithms.data_structures.interfaces.IMap; + +public class MapTest { + + @SuppressWarnings("unchecked") + public static > boolean testMap(IMap map, Class type, String name, + Integer[] data, Integer _invalid) { + for (int i = 0; i < data.length; i++) { + Integer item = data[i]; + K k = null; + V v = null; + if (type.isAssignableFrom(Integer.class)) { + k = (K)item; + v = (V)Utils.parseT(item, type); + } else if (type.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, type); + v = (V)item; + } + V added = map.put(k,v); + if ((!map.validate() || (map.size() != (i+1)))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,map); + return false; + } + if ((added!=null || !map.contains(k))) { + System.err.println(name+" YIKES!! " + item + " doesn't exists."); + Utils.handleError(data,map); + return false; + } + } + + K invalidKey = null; + if (type.isAssignableFrom(Integer.class)) { + invalidKey = (K)Utils.parseT(_invalid, type); + } else if (type.isAssignableFrom(String.class)) { + invalidKey = (K)Utils.parseT(_invalid, type); + } + boolean contains = map.contains(invalidKey); + V removed = map.remove(invalidKey); + if (contains || (removed!=null)) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,map); + return false; + } + + for (int i = 0; i < data.length; i++) { + Integer item = data[i]; + K k = null; + if (type.isAssignableFrom(Integer.class)) { + k = (K)item; + } else if (type.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, type); + } + removed = map.remove(k); + if ((!map.validate() || (map.size() != (data.length-(i+1))))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,map); + return false; + } + if (map.contains(k)) { + System.err.println(name+" YIKES!! " + item + " still exists."); + Utils.handleError(data,map); + return false; + } + } + + // Add half, remove a quarter, add three-quarters, remove all + int quarter = data.length/4; + int half = data.length/2; + for (int i = 0; i < half; i++) { + Integer item = data[i]; + K k = null; + V v = null; + if (type.isAssignableFrom(Integer.class)) { + k = (K)item; + v = (V)Utils.parseT(item, type); + } else if (type.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, type); + v = (V)item; + } + V added = map.put(k,v); + if ((!map.validate() || (map.size() != (i+1)))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,map); + return false; + } + if ((added!=null || !map.contains(k))) { + System.err.println(name+" YIKES!! " + item + " doesn't exists."); + Utils.handleError(data,map); + return false; + } + } + for (int i = (half-1); i >= quarter; i--) { + Integer item = data[i]; + K k = null; + if (type.isAssignableFrom(Integer.class)) { + k = (K)item; + } else if (type.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, type); + } + removed = map.remove(k); + if ((!map.validate() || (map.size() != i))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,map); + return false; + } + if ((removed==null || map.contains(k))) { + System.err.println(name+" YIKES!! " + item + " still exists."); + Utils.handleError(data,map); + return false; + } + } + for (int i = quarter; i < data.length; i++) { + Integer item = data[i]; + K k = null; + V v = null; + if (type.isAssignableFrom(Integer.class)) { + k = (K)item; + v = (V)Utils.parseT(item, type); + } else if (type.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, type); + v = (V)item; + } + V added = map.put(k,v); + if ((!map.validate() || (map.size() != (i+1)))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,map); + return false; + } + if ((added!=null || !map.contains(k))) { + System.err.println(name+" YIKES!! " + item + " doesn't exists."); + Utils.handleError(data,map); + return false; + } + } + for (int i = data.length-1; i >= 0; i--) { + Integer item = data[i]; + K k = null; + if (type.isAssignableFrom(Integer.class)) { + k = (K)item; + } else if (type.isAssignableFrom(String.class)) { + k = (K)Utils.parseT(item, type); + } + removed = map.remove(k); + if ((!map.validate() || (map.size() != i))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,map); + return false; + } + if ((removed==null || map.contains(k))) { + System.err.println(name+" YIKES!! " + item + " still exists."); + Utils.handleError(data,map); + return false; + } + } + + if ((map.size() != 0)) { + System.err.println(name+" YIKES!! a size mismatch."); + Utils.handleError(data,map); + return false; + } + + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/QueueTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/QueueTest.java new file mode 100644 index 00000000..5bcef7f1 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/QueueTest.java @@ -0,0 +1,152 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import com.jwetherell.algorithms.data_structures.interfaces.IQueue; + +public class QueueTest { + + public static > boolean testQueue(IQueue queue, String name, + T[] data, T _invalid) { + for (int i = 0; i < data.length; i++) { + T item = data[i]; + boolean added = queue.offer(item); + if (!queue.validate() || (queue.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,queue); + return false; + } + if (!added || !queue.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,queue); + return false; + } + } + + boolean contains = queue.contains(_invalid); + boolean removed = queue.remove(_invalid); + if (contains || removed) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,queue); + return false; + } + + int size = queue.size(); + for (int i = 0; i < size; i++) { + T item = queue.poll(); + T correct = data[i]; + if (item.compareTo(correct)!=0) { + System.err.println(name+" YIKES!! " + item + " does not match FIFO item."); + Utils.handleError(data,queue); + return false; + } + if (!queue.validate() || (queue.size() != data.length-(i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,queue); + return false; + } + if (queue.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,queue); + return false; + } + } + + // Add half, remove a quarter, add three-quarters + int quarter = data.length/4; + int half = data.length/2; + int changeOver = half-quarter; + for (int i = 0; i < half; i++) { + T item = data[i]; + boolean added = queue.offer(item); + if (!queue.validate() || (queue.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,queue); + return false; + } + if (!added || !queue.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,queue); + return false; + } + } + for (int i = 0; i < quarter; i++) { + T item = queue.poll(); + T correct = data[i]; + if (item.compareTo(correct)!=0) { + System.err.println(name+" YIKES!! " + item + " does not match FIFO item."); + Utils.handleError(data,queue); + return false; + } + if (!queue.validate() || (queue.size() != (half-(i+1)))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,queue); + return false; + } + if (queue.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,queue); + return false; + } + } + for (int i = 0; i < quarter; i++) { + T item = data[i]; + boolean added = queue.offer(item); + if (!queue.validate() || (queue.size() != ((half-quarter)+(i+1)))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,queue); + return false; + } + if (!added || !queue.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,queue); + return false; + } + } + for (int i = half; i < data.length; i++) { + T item = data[i]; + boolean added = queue.offer(item); + if (!queue.validate() || (queue.size() != (i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,queue); + return false; + } + if (!added || !queue.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,queue); + return false; + } + } + for (int i = 0; i < data.length; i++) { + T item = queue.poll(); + int idx = i; + if (idx < changeOver) { + idx = quarter+i; + } else if (idx>=changeOver && idx> boolean testSet(ISet set, String name, + T[] data, T _invalid) { + for (int i = 0; i < data.length; i++) { + T item = data[i]; + boolean added = set.add(item); + if (!set.validate() || (set.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,set); + return false; + } + if (!added || !set.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,set); + return false; + } + } + + boolean contains = set.contains(_invalid); + boolean removed = set.remove(_invalid); + if (contains || removed) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,set); + return false; + } + + int size = set.size(); + for (int i = 0; i < size; i++) { + T item = data[i]; + removed = set.remove(item); + if (!set.validate() || (set.size() != data.length-(i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,set); + return false; + } + if (!removed || set.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,set); + return false; + } + } + + // Add half, remove a quarter, add three-quarters, remove all + int quarter = data.length/4; + int half = data.length/2; + for (int i = 0; i < half; i++) { + T item = data[i]; + boolean added = set.add(item); + if (!set.validate() || (set.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,set); + return false; + } + if (!added || !set.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,set); + return false; + } + } + for (int i = (half-1); i >= quarter; i--) { + T item = data[i]; + removed = set.remove(item); + if (!set.validate() || (set.size() != i)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,set); + return false; + } + if (!removed || set.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,set); + return false; + } + } + for (int i = quarter; i < data.length; i++) { + T item = data[i]; + boolean added = set.add(item); + if (!set.validate() || (set.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,set); + return false; + } + if (!added || !set.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,set); + return false; + } + } + for (int i = data.length-1; i >= 0; i--) { + T item = data[i]; + removed = set.remove(item); + if (!set.validate() || (set.size() != i)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,set); + return false; + } + if ((!removed || set.contains(item))) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,set); + return false; + } + } + + if (set.size() != 0) { + System.err.println(name+" YIKES!! a size mismatch."); + Utils.handleError(data,set); + return false; + } + + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/StackTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/StackTest.java new file mode 100644 index 00000000..57554dba --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/StackTest.java @@ -0,0 +1,131 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import com.jwetherell.algorithms.data_structures.interfaces.IStack; + +public class StackTest { + + public static > boolean testStack(IStack stack, String name, + T[] data, T _invalid) { + for (int i = 0; i < data.length; i++) { + T item = data[i]; + boolean added = stack.push(item); + if (!stack.validate() || (stack.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,stack); + return false; + } + if (!added || item==null || !stack.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,stack); + return false; + } + } + + boolean contains = stack.contains(_invalid); + boolean removed = stack.remove(_invalid); + if (contains || removed) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,stack); + return false; + } + + int size = stack.size(); + for (int i = 0; i < size; i++) { + T item = stack.pop(); + T correct = data[data.length-(i+1)]; + if ((item.compareTo(correct)!=0)) { + System.err.println(name+" YIKES!! " + item + " does not match LIFO item."); + Utils.handleError(data,stack); + return false; + } + if (!stack.validate() || (stack.size() != data.length-(i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,stack); + return false; + } + if (stack.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,stack); + return false; + } + } + + // Add half, remove a quarter, add three-quarters, remove all + int quarter = data.length/4; + int half = data.length/2; + for (int i = 0; i < half; i++) { + T item = data[i]; + boolean added = stack.push(item); + if (!stack.validate() || (stack.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,stack); + return false; + } + if (!added || item==null || !stack.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,stack); + return false; + } + } + for (int i = (half-1); i >= quarter; i--) { + T item = stack.pop(); + T correct = data[i]; + if (item.compareTo(correct)!=0) { + System.err.println(name+" YIKES!! " + item + " does not match LIFO item."); + Utils.handleError(data,stack); + return false; + } + if (!stack.validate() || (stack.size() != i)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,stack); + return false; + } + if (stack.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,stack); + return false; + } + } + for (int i = quarter; i < data.length; i++) { + T item = data[i]; + boolean added = stack.push(item); + if (!stack.validate() || (stack.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,stack); + return false; + } + if (!added || item==null || !stack.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,stack); + return false; + } + } + for (int i = data.length-1; i >= 0; i--) { + T item = stack.pop(); + T correct = data[i]; + if (item.compareTo(correct)!=0) { + System.err.println(name+" YIKES!! " + item + " does not match LIFO item."); + Utils.handleError(data,stack); + return false; + } + if (!stack.validate() || (stack.size() != i)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,stack); + return false; + } + if (stack.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,stack); + return false; + } + } + + if (stack.size() != 0) { + System.err.println(name+" YIKES!! a size mismatch."); + Utils.handleError(data,stack); + return false; + } + + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/SuffixTreeTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/SuffixTreeTest.java new file mode 100644 index 00000000..c0074eaa --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/SuffixTreeTest.java @@ -0,0 +1,44 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import com.jwetherell.algorithms.data_structures.interfaces.ISuffixTree; + +public class SuffixTreeTest { + + /** + * In computer science, a suffix tree (also called PAT tree or, in an earlier + * form, position tree) is a compressed trie containing all the suffixes of + * the given text as their keys and positions in the text as their values. + * Suffix trees allow particularly fast implementations of many important + * string operations. + * + * @param tree Suffix tree to test. + * @param test String to use in testing the suffix tree. + * @return True if the suffix tree passes it's invariants tests. + */ + public static boolean suffixTreeTest(ISuffixTree tree, String test) { + boolean exists = tree.doesSubStringExist(test); + if (!exists) { + System.err.println("YIKES!! " + test + " doesn't exists."); + Utils.handleError(test,tree); + return false; + } + + String failed = test+"Z"; + exists = tree.doesSubStringExist(failed); + if (exists) { + System.err.println("YIKES!! " + failed + " exists."); + Utils.handleError(failed,tree); + return false; + } + + String pass = test.substring(0, 6); + exists = tree.doesSubStringExist(pass); + if (!exists) { + System.err.println("YIKES!! " + pass + " doesn't exists."); + Utils.handleError(pass,tree); + return false; + } + + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/TreeTest.java b/test/com/jwetherell/algorithms/data_structures/test/common/TreeTest.java new file mode 100644 index 00000000..6df37169 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/TreeTest.java @@ -0,0 +1,123 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import com.jwetherell.algorithms.data_structures.interfaces.ITree; + +public class TreeTest { + + public static > boolean testTree(ITree tree, Class type, String name, + Integer[] data, Integer _invalid) { + for (int i = 0; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + boolean added = tree.add(item); + if (!tree.validate() || (tree.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,tree); + return false; + } + if (!added || !tree.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,tree); + return false; + } + } + + T invalidItem = Utils.parseT(_invalid, type); + boolean contains = tree.contains(invalidItem); + T removed = tree.remove(invalidItem); + if (contains || removed!=null) { + System.err.println(name+" invalidity check. contains=" + contains + " removed=" + removed); + Utils.handleError(_invalid,tree); + return false; + } + + int size = tree.size(); + for (int i = 0; i < size; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + removed = tree.remove(item); + if (!tree.validate() || (tree.size() != data.length-(i+1))) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,tree); + return false; + } + if (removed==null || tree.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been removed."); + Utils.handleError(data,tree); + return false; + } + } + + // Add half, remove a quarter, add three-quarters + int quarter = data.length/4; + int half = data.length/2; + for (int i = 0; i < half; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + boolean added = tree.add(item); + if (!tree.validate() || (tree.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,tree); + return false; + } + if (!added || !tree.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,tree); + return false; + } + } + for (int i = (half-1); i >= quarter; i--) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + removed = tree.remove(item); + if (!tree.validate() || (tree.size() != i)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,tree); + return false; + } + if (removed==null || tree.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,tree); + return false; + } + } + for (int i = quarter; i < data.length; i++) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + boolean added = tree.add(item); + if (!tree.validate() || (tree.size() != i+1)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,tree); + return false; + } + if (!added || !tree.contains(item)) { + System.err.println(name+" YIKES!! " + item + " doesn't exists but has been added."); + Utils.handleError(data,tree); + return false; + } + } + for (int i = data.length-1; i >= 0; i--) { + Integer value = data[i]; + T item = Utils.parseT(value, type); + removed = tree.remove(item); + if (!tree.validate() || (tree.size() != i)) { + System.err.println(name+" YIKES!! " + item + " caused a size mismatch."); + Utils.handleError(data,tree); + return false; + } + if (removed==null || tree.contains(item)) { + System.err.println(name+" YIKES!! " + item + " still exists but it has been remove."); + Utils.handleError(data,tree); + return false; + } + } + + if (tree.size() != 0) { + System.err.println(name+" YIKES!! a size mismatch."); + Utils.handleError(data,tree); + return false; + } + + return true; + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/test/common/Utils.java b/test/com/jwetherell/algorithms/data_structures/test/common/Utils.java new file mode 100644 index 00000000..83ec3fd4 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/test/common/Utils.java @@ -0,0 +1,136 @@ +package com.jwetherell.algorithms.data_structures.test.common; + +import java.util.Arrays; +import java.util.Random; + +public class Utils { + + public static final T parseT(final Integer value, final Class type) { + T returnValue = null; + + if (type == null) { + throw new NullPointerException("Type can not be null"); + } else if(Integer.class.equals(type)) { + returnValue = type.cast(value); + } else if(String.class.equals(type)) { + returnValue = type.cast(String.valueOf(value)); + } else { + throw new IllegalArgumentException("Unsupported type " + type.getName()); + } + return returnValue; + } + + public static void handleError(Object obj) { + System.err.println("Object={\n"+obj.toString()+"\n}"); + throw new RuntimeException("Error in test."); + } + + public static void handleError(Object data, Object obj) { + System.err.println("Data={"+data+"}"); + System.err.println("Object={\n"+obj.toString()+"\n}"); + throw new RuntimeException("Error in test."); + } + + public static void handleError(Object[] data, Object obj) { + System.err.println("Data={"); + for (Object o : data) + System.err.print(o.toString()+", "); + System.err.println("\n}"); + System.err.println("Object={\n"+obj.toString()+"\n}"); + throw new RuntimeException("Error in test."); + } + + private static final Random RANDOM = new Random(); + + public static TestData testData(int... integers) { + TestData data = new TestData(integers.length); + + StringBuilder builder = new StringBuilder(); + data.unsorted = new Integer[integers.length]; + java.util.Set set = new java.util.HashSet(); + builder.append("Array="); + for (int i = 0; i < integers.length; i++) { + Integer j = integers[i]; + data.unsorted[i] = j; + if (i != integers.length-1) + builder.append(j).append(','); + } + set.clear(); + set = null; + builder.append('\n'); + data.string = builder.toString(); + + data.sorted = Arrays.copyOf(data.unsorted, data.unsorted.length); + Arrays.sort(data.sorted); + + return data; + } + + public static TestData generateTestData(int data_size) { + TestData data = new TestData(data_size); + + StringBuilder builder = new StringBuilder(); + data.unsorted = new Integer[data_size]; + java.util.Set set = new java.util.HashSet(); + builder.append("Array="); + for (int i = 0; i < data_size; i++) { + Integer j = RANDOM.nextInt(data.random_size); + // Make sure there are no duplicates + boolean found = true; + while (found) { + if (set.contains(j)) { + j = RANDOM.nextInt(data.random_size); + } else { + data.unsorted[i] = j; + set.add(j); + found = false; + } + } + data.unsorted[i] = j; + if (i != data_size-1) + builder.append(j).append(','); + } + set.clear(); + set = null; + builder.append('\n'); + data.string = builder.toString(); + + data.sorted = Arrays.copyOf(data.unsorted, data.unsorted.length); + Arrays.sort(data.sorted); + + return data; + } + + public static class TestData { + + public int random_size = 0; + public Integer invalid = 0; + public Integer[] unsorted = null; + public Integer[] sorted = null; + public String string = null; + + public TestData(int size) { + this.random_size = 1000 * size; + this.invalid = random_size + 10; + } + + public TestData(Integer[] _unsorted) { + this(_unsorted.length); + unsorted = _unsorted; + sorted = unsorted.clone(); + Arrays.sort(sorted); + setString(unsorted); + } + + private static final String setString(Integer[] _unsorted) { + StringBuilder builder = new StringBuilder(); + builder.append("Array="); + for (int i=0; i < _unsorted.length; i++) { + Integer d = _unsorted[i]; + if (i != _unsorted.length-1) builder.append(d).append(','); + } + builder.append('\n'); + return builder.toString(); + } + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/timing/DataStructuresTiming.java b/test/com/jwetherell/algorithms/data_structures/timing/DataStructuresTiming.java new file mode 100644 index 00000000..61b82e88 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/timing/DataStructuresTiming.java @@ -0,0 +1,1885 @@ +package com.jwetherell.algorithms.data_structures.timing; + +import java.lang.reflect.Array; +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Formatter; +import java.util.Locale; +import java.util.NavigableSet; +import java.util.Random; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.ConcurrentSkipListSet; + +import com.jwetherell.algorithms.data_structures.AVLTree; +import com.jwetherell.algorithms.data_structures.BTree; +import com.jwetherell.algorithms.data_structures.BinaryHeap; +import com.jwetherell.algorithms.data_structures.BinarySearchTree; +import com.jwetherell.algorithms.data_structures.HashArrayMappedTrie; +import com.jwetherell.algorithms.data_structures.HashMap; +import com.jwetherell.algorithms.data_structures.List; +import com.jwetherell.algorithms.data_structures.PatriciaTrie; +import com.jwetherell.algorithms.data_structures.Queue; +import com.jwetherell.algorithms.data_structures.RadixTrie; +import com.jwetherell.algorithms.data_structures.RedBlackTree; +import com.jwetherell.algorithms.data_structures.SkipList; +import com.jwetherell.algorithms.data_structures.SkipListMap; +import com.jwetherell.algorithms.data_structures.SplayTree; +import com.jwetherell.algorithms.data_structures.Stack; +import com.jwetherell.algorithms.data_structures.Treap; +import com.jwetherell.algorithms.data_structures.TreeMap; +import com.jwetherell.algorithms.data_structures.Trie; +import com.jwetherell.algorithms.data_structures.TrieMap; +import com.jwetherell.algorithms.data_structures.test.common.Utils; + +public class DataStructuresTiming { + + private static final Random RANDOM = new Random(); + private static final DecimalFormat FORMAT = new DecimalFormat("0.##"); + private static final int NUMBER_OF_TESTS = 3; // There will always be NUMBER_OF_TESTS+1 runs since the first round is thrown away (JITing) + private static final int ARRAY_SIZE = 1024*10; // Number of items to add/remove/look-up from each data structure + private static final int RANDOM_SIZE = 1000 * ARRAY_SIZE; + private static final Integer INVALID = RANDOM_SIZE + 10; + + private static final int TESTS = 40; // Max number of dynamic data structures to test + private static final String[] TEST_NAMES = new String[TESTS]; // Array to hold the test names + private static final long[][] TEST_RESULTS = new long[TESTS][]; // Array to hold the test results + + private static int debug = 1; // Debug level. 0=None, 1=Time and Memory (if enabled), 2=Time, Memory, data structure debug + private static boolean debugTime = true; // How much time to: add all, remove all, add all items in reverse order, remove all + private static boolean debugMemory = true; // How much memory is used by the data structure + + private static int testIndex = 0; // Index into the tests + private static boolean firstTimeThru = true; // We throw away the first set of data to avoid JITing + + public static void main(String[] args) { + System.out.println("Starting tests."); + boolean passed = false; + try { + passed = runTests(); + } catch (NullPointerException e) { + throw e; + } + if (passed) System.out.println("Tests finished. All passed."); + else System.err.println("Tests finished. Detected a failure."); + } + + private static void generateTestData(int idx, int size, Integer[][] unsorteds, Integer[][] sorteds, String[] strings) { + System.out.println("Generating data."); + StringBuilder builder = new StringBuilder(); + builder.append("Array="); + java.util.Set set = new java.util.HashSet(); + unsorteds[idx] = new Integer[size]; + sorteds[idx] = new Integer[size]; + for (int i = 0; i < size; i++) { + Integer j = RANDOM.nextInt(RANDOM_SIZE); + // Make sure there are no duplicates + boolean found = true; + while (found) { + if (set.contains(j)) { + j = RANDOM.nextInt(RANDOM_SIZE); + } else { + unsorteds[idx][i] = j; + set.add(j); + found = false; + } + } + unsorteds[idx][i] = j; + if (i!=size-1) builder.append(j).append(','); + } + set.clear(); + set = null; + builder.append('\n'); + strings[idx] = builder.toString(); + if (debug > 1) System.out.println(strings[idx]); + + sorteds[idx] = Arrays.copyOf(unsorteds[idx], unsorteds[idx].length); + Arrays.sort(sorteds[idx]); + + System.out.println("Generated data."); + } + + private static boolean runTests() { + testIndex = 0; + + // requested number of tests plus the warm-up round + int tests = NUMBER_OF_TESTS+1; + Integer[][] unsorteds = new Integer[tests][]; + Integer[][] sorteds = new Integer[tests][]; + String[] strings = new String[tests]; + for (int i=0; i avlTree = new AVLTree(); + Collection bstCollection = avlTree.toCollection(); + if (!testJavaCollection(bstCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestBTree extends Testable { + String name = "B-Tree "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + BTree bTree = new BTree(4); + Collection bstCollection = bTree.toCollection(); + if (!testJavaCollection(bstCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestBST extends Testable { + String name = "BST "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + BinarySearchTree bst = new BinarySearchTree(); + Collection bstCollection = bst.toCollection(); + if (!testJavaCollection(bstCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestMinHeapArray extends Testable { + String name = "Min-Heap [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + BinaryHeap.BinaryHeapArray aHeapMin = new BinaryHeap.BinaryHeapArray(BinaryHeap.Type.MIN); + Collection aCollectionMin = aHeapMin.toCollection(); + if (!testJavaCollection(aCollectionMin,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestMinHeapTree extends Testable { + String name = "Min-Heap [tree]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + BinaryHeap.BinaryHeapTree tHeapMin = new BinaryHeap.BinaryHeapTree(BinaryHeap.Type.MIN); + Collection tCollectionMin = tHeapMin.toCollection(); + if (!testJavaCollection(tCollectionMin,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestMaxHeapArray extends Testable { + String name = "Max-Heap [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + BinaryHeap.BinaryHeapArray aHeapMax = new BinaryHeap.BinaryHeapArray(BinaryHeap.Type.MAX); + Collection aCollectionMax = aHeapMax.toCollection(); + if (!testJavaCollection(aCollectionMax,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestMaxHeapTree extends Testable { + String name = "Max-Heap [tree]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + BinaryHeap.BinaryHeapTree tHeapMax = new BinaryHeap.BinaryHeapTree(BinaryHeap.Type.MAX); + Collection tCollectionMax = tHeapMax.toCollection(); + if (!testJavaCollection(tCollectionMax,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestHashMapProbing extends Testable { + String name = "Probing HashMap "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + HashMap pHashMap = new HashMap(HashMap.Type.PROBING, ARRAY_SIZE/2); + java.util.Map jMap = pHashMap.toMap(); + if (!testJavaMap(jMap,Integer.class,String.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestHashMapChaining extends Testable { + String name = "Chaining HashMap "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + HashMap cHashMap = new HashMap(HashMap.Type.CHAINING, ARRAY_SIZE/2); + java.util.Map jMap = cHashMap.toMap(); + if (!testJavaMap(jMap,Integer.class,String.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestHAMT extends Testable { + String name = "HAMT "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + HashArrayMappedTrie hamt = new HashArrayMappedTrie(); + java.util.Map jMap = hamt.toMap(); + if (!testJavaMap(jMap,Integer.class,String.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaHashMap extends Testable { + String name = "Java's HashMap "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.Map javaHashMap = new java.util.HashMap(ARRAY_SIZE/2); + if (!testJavaMap(javaHashMap,Integer.class,String.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaMinHeap extends Testable { + Comparator comparator = new Comparator() { + @Override + public int compare(Integer arg0, Integer arg1) { + if (arg0.compareTo(arg1) > 0) + return 1; + else if (arg1.compareTo(arg0) > 0) + return -1; + return 0; + } + }; + String name = "Java's Min-Heap [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.PriorityQueue javaMinArrayHeap = new java.util.PriorityQueue(10, comparator); + if (!testJavaCollection(javaMinArrayHeap,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaMaxHeap extends Testable { + Comparator comparator = new Comparator() { + @Override + public int compare(Integer arg0, Integer arg1) { + if (arg0.compareTo(arg1) > 0) + return -1; + else if (arg1.compareTo(arg0) > 0) + return 1; + return 0; + } + }; + String name = "Java's Max-Heap [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.PriorityQueue javaMaxArrayHeap = new java.util.PriorityQueue(10, comparator); + if (!testJavaCollection(javaMaxArrayHeap,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaArrayList extends Testable { + String name = "Java's List [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.List javaArrayList = new java.util.ArrayList(); + if (!testJavaCollection(javaArrayList,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaLinkedList extends Testable { + String name = "Java's List [linked]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.List javaLinkedList = new java.util.LinkedList(); + if (!testJavaCollection(javaLinkedList,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaArrayQueue extends Testable { + String name = "Java's Queue [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.Deque javaArrayQueue = new java.util.ArrayDeque(); + if (!testJavaCollection(javaArrayQueue,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaLinkedQueue extends Testable { + String name = "Java's Queue [linked]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.Deque javaLinkedQueue = new java.util.LinkedList(); + if (!testJavaCollection(javaLinkedQueue,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaRedBlackIntegerTree extends Testable { + String name = "Java's Red-Black Tree "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.TreeSet javaRedBlackTreeInteger = new java.util.TreeSet(); + if (!testJavaCollection(javaRedBlackTreeInteger,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaRedBlackStringTree extends Testable { + String name = "Java's Red-Black Tree "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.TreeSet javaRedBlackTreeString = new java.util.TreeSet(); + if (!testJavaCollection(javaRedBlackTreeString,String.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaStack extends Testable { + String name = "Java's Stack [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.Stack javaStack = new java.util.Stack(); + if (!testJavaCollection(javaStack,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaTreeMap extends Testable { + String name = "Java's TreeMap "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + java.util.Map javaTreeMap = new java.util.TreeMap(); + if (!testJavaMap(javaTreeMap,String.class,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestArrayList extends Testable { + String name = "List [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + List.ArrayList arrayList = new List.ArrayList(); + Collection aCollection = arrayList.toCollection(); + if (!testJavaCollection(aCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestSinglyLinkedList extends Testable { + String name = "List [singly linked]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + List.SinglyLinkedList linkedList = new List.SinglyLinkedList(); + Collection lCollection = linkedList.toCollection(); + if (!testJavaCollection(lCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestDoublyLinkedList extends Testable { + String name = "List [doubly linked]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + List.DoublyLinkedList linkedList = new List.DoublyLinkedList(); + Collection lCollection = linkedList.toCollection(); + if (!testJavaCollection(lCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestPatriciaTrie extends Testable { + String name = "PatriciaTrie "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + PatriciaTrie patriciaTrie = new PatriciaTrie(); + Collection bstCollection = patriciaTrie.toCollection(); + if (!testJavaCollection(bstCollection,String.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestArrayQueue extends Testable { + String name = "Queue [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + Queue.ArrayQueue arrayQueue = new Queue.ArrayQueue(); + Collection aCollection = arrayQueue.toCollection(); + if (!testJavaCollection(aCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestLinkedQueue extends Testable { + String name = "Queue [linked]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + Queue.LinkedQueue linkedQueue = new Queue.LinkedQueue(); + Collection lCollection = linkedQueue.toCollection(); + if (!testJavaCollection(lCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestRadixTrie extends Testable { + String name = "RadixTrie "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + RadixTrie radixTrie = new RadixTrie(); + java.util.Map jMap = radixTrie.toMap(); + if (!testJavaMap(jMap,String.class,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestRedBlackTree extends Testable { + String name = "Red-Black Tree "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + BinarySearchTree redBlackTree = new RedBlackTree(); + Collection bstCollection = redBlackTree.toCollection(); + if (!testJavaCollection(bstCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaSkipList extends Testable { + String name = "Java's SkipListSet "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + NavigableSet javaSkipList = new ConcurrentSkipListSet(); + Collection lCollection = javaSkipList; + if (!testJavaCollection(lCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestSkipList extends Testable { + String name = "SkipList "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + SkipList skipList = new SkipList(); + Collection lCollection = skipList.toCollection(); + if (!testJavaCollection(lCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestSplayTree extends Testable { + String name = "Splay Tree "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + BinarySearchTree splayTree = new SplayTree(); + Collection bstCollection = splayTree.toCollection(); + if (!testJavaCollection(bstCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestArrayStack extends Testable { + String name = "Stack [array]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + Stack.ArrayStack arrayStack = new Stack.ArrayStack(); + Collection aCollection = arrayStack.toCollection(); + if (!testJavaCollection(aCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestLinkedStack extends Testable { + String name = "Stack [linked]"; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + Stack.LinkedStack linkedStack = new Stack.LinkedStack(); + Collection lCollection = linkedStack.toCollection(); + if (!testJavaCollection(lCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestTreap extends Testable { + String name = "Treap "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + BinarySearchTree treap = new Treap(); + Collection treapCollection = treap.toCollection(); + if (!testJavaCollection(treapCollection,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestTreeMap extends Testable { + String name = "TreeMap "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + TreeMap treeMap = new TreeMap(); + java.util.Map jMap = treeMap.toMap(); + if (!testJavaMap(jMap,String.class,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestTrie extends Testable { + String name = "Trie "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + Trie trie = new Trie(); + Collection trieCollection = trie.toCollection(); + if (!testJavaCollection(trieCollection,String.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestTrieMap extends Testable { + String name = "TrieMap "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + TrieMap trieMap = new TrieMap(); + java.util.Map jMap = trieMap.toMap(); + if (!testJavaMap(jMap,String.class,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestJavaSkipListMap extends Testable { + String name = "Java's SkipListMap "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + ConcurrentSkipListMap javaSkipListMap = new ConcurrentSkipListMap(); + if (!testJavaMap(javaSkipListMap,String.class,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + private static class TestSkipListMap extends Testable { + String name = "SkipListMap "; + + public String getName() { + return name; + } + + public boolean run(Integer[] unsorted, Integer[] sorted, String input) { + this.input = input; + SkipListMap skipListMap = new SkipListMap(); + java.util.Map jMap = skipListMap.toMap(); + if (!testJavaMap(jMap,String.class,Integer.class,name, unsorted, sorted, input)) return false; + return true; + } + + } + + @SuppressWarnings("unchecked") + private static > boolean testJavaCollection(Collection collection, Class type, String name, Integer[] _unsorted, Integer[] _sorted, String input) { + // Make sure the collection is empty + if (!collection.isEmpty()) { + System.err.println(name+" initial isEmpty() failed."); + handleError(input,collection); + return false; + } + if (collection.size()!=0) { + System.err.println(name+" initial size() failed."); + handleError(input,collection); + return false; + } + + T[] unsorted = (T[]) Array.newInstance(type, _unsorted.length); + T[] sorted = (T[]) Array.newInstance(type, _sorted.length); + for (int i=0; i 1) System.out.println(name); + + // Throw away first test to remove JITing + if (!firstTimeThru) + TEST_NAMES[testIndex] = name; + + unsortedCount++; + { // UNSORTED: Add and remove in order (from index zero to length) + beforeMemory = 0L; + afterMemory = 0L; + beforeAddTime = 0L; + afterAddTime = 0L; + if (debugMemory) beforeMemory = DataStructuresTiming.getMemoryUse(); + if (debugTime) beforeAddTime = System.nanoTime(); + for (int i = 0; i < unsorted.length; i++) { + T item = unsorted[i]; + boolean added = collection.add(item); + if (!added) { + System.err.println(name+" unsorted add failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterAddTime = System.nanoTime(); + addTime += afterAddTime - beforeAddTime; + if (debug > 0) System.out.println(name+" unsorted add time = " + (addTime / unsortedCount) + " ns"); + } + if (debugMemory) { + afterMemory = DataStructuresTiming.getMemoryUse(); + memory += afterMemory - beforeMemory; + if (debug > 0) System.out.println(name+" unsorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); + } + + if (debug > 1) System.out.println(collection.toString()); + + beforeLookupTime = 0L; + afterLookupTime = 0L; + if (debugTime) beforeLookupTime = System.nanoTime(); + for (int i = 0; i < unsorted.length; i++) { + T item = unsorted[i]; + boolean contains = collection.contains(item); + if (!contains) { + System.err.println(name+" unsorted contains failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterLookupTime = System.nanoTime(); + lookupTime += afterLookupTime - beforeLookupTime; + if (debug > 0) System.out.println(name+" unsorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); + } + + beforeRemoveTime = 0L; + afterRemoveTime = 0L; + if (debugTime) beforeRemoveTime = System.nanoTime(); + for (int i = 0; i < unsorted.length; i++) { + T item = unsorted[i]; + boolean removed = collection.remove(item); + if (!removed) { + System.err.println(name+" unsorted remove failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterRemoveTime = System.nanoTime(); + removeTime += afterRemoveTime - beforeRemoveTime; + if (debug > 0) System.out.println(name+" unsorted remove time = " + (removeTime / unsortedCount) + " ns"); + } + + if (!collection.isEmpty()) { + System.err.println(name+" unsorted isEmpty() failed."); + handleError(input,collection); + return false; + } + if (collection.size()!=0) { + System.err.println(name+" unsorted size() failed."); + handleError(input,collection); + return false; + } + } + + unsortedCount++; + { // UNSORTED: Add in reverse (from index length-1 to zero) order and then remove in order (from index zero to length) + beforeMemory = 0L; + afterMemory = 0L; + beforeAddTime = 0L; + afterAddTime = 0L; + if (debugMemory) beforeMemory = DataStructuresTiming.getMemoryUse(); + if (debugTime) beforeAddTime = System.nanoTime(); + for (int i = unsorted.length - 1; i >= 0; i--) { + T item = unsorted[i]; + boolean added = collection.add(item); + if (!added) { + System.err.println(name+" unsorted add failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterAddTime = System.nanoTime(); + addTime += afterAddTime - beforeAddTime; + if (debug > 0) System.out.println(name+" unsorted add time = " + (addTime / unsortedCount) + " ns"); + } + if (debugMemory) { + afterMemory = DataStructuresTiming.getMemoryUse(); + memory += afterMemory - beforeMemory; + if (debug > 0) System.out.println(name+" unsorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); + } + + if (debug > 1) System.out.println(collection.toString()); + + beforeLookupTime = 0L; + afterLookupTime = 0L; + if (debugTime) beforeLookupTime = System.nanoTime(); + for (int i = 0; i < unsorted.length; i++) { + T item = unsorted[i]; + boolean contains = collection.contains(item); + if (!contains) { + System.err.println(name+" unsorted contains failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterLookupTime = System.nanoTime(); + lookupTime += afterLookupTime - beforeLookupTime; + if (debug > 0) System.out.println(name+" unsorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); + } + + beforeRemoveTime = 0L; + afterRemoveTime = 0L; + if (debugTime) beforeRemoveTime = System.nanoTime(); + for (int i = 0; i < unsorted.length; i++) { + T item = unsorted[i]; + boolean removed = collection.remove(item); + if (!removed) { + System.err.println(name+" unsorted remove failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterRemoveTime = System.nanoTime(); + removeTime += afterRemoveTime - beforeRemoveTime; + if (debug > 0) System.out.println(name+" unsorted remove time = " + (removeTime / unsortedCount) + " ns"); + } + + if (!collection.isEmpty()) { + System.err.println(name+" unsorted isEmpty() failed."); + handleError(input,collection); + return false; + } + if (collection.size()!=0) { + System.err.println(name+" unsorted size() failed."); + handleError(input,collection); + return false; + } + } + + long addSortedTime = 0L; + long removeSortedTime = 0L; + + long beforeAddSortedTime = 0L; + long afterAddSortedTime = 0L; + + long beforeRemoveSortedTime = 0L; + long afterRemoveSortedTime = 0L; + + sortedCount++; + { // SORTED: Add and remove in order (from index zero to length) + beforeMemory = 0L; + afterMemory = 0L; + beforeAddSortedTime = 0L; + afterAddSortedTime = 0L; + if (debugMemory) beforeMemory = DataStructuresTiming.getMemoryUse(); + if (debugTime) beforeAddSortedTime = System.nanoTime(); + for (int i = 0; i < sorted.length; i++) { + T item = sorted[i]; + boolean added = collection.add(item); + if (!added) { + System.err.println(name+" sorted add failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterAddSortedTime = System.nanoTime(); + addSortedTime += afterAddSortedTime - beforeAddSortedTime; + if (debug > 0) System.out.println(name+" sorted add time = " + (addSortedTime / sortedCount) + " ns"); + } + if (debugMemory) { + afterMemory = DataStructuresTiming.getMemoryUse(); + memory += afterMemory - beforeMemory; + if (debug > 0) System.out.println(name+" sorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); + } + + if (debug > 1) System.out.println(collection.toString()); + + beforeLookupTime = 0L; + afterLookupTime = 0L; + if (debugTime) beforeLookupTime = System.nanoTime(); + for (int i = 0; i < sorted.length; i++) { + T item = sorted[i]; + boolean contains = collection.contains(item); + if (!contains) { + System.err.println(name+" sorted contains failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterLookupTime = System.nanoTime(); + lookupTime += afterLookupTime - beforeLookupTime; + if (debug > 0) System.out.println(name+" sorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); + } + + beforeRemoveSortedTime = 0L; + afterRemoveSortedTime = 0L; + if (debugTime) beforeRemoveSortedTime = System.nanoTime(); + for (int i = 0; i < sorted.length; i++) { + T item = sorted[i]; + boolean removed = collection.remove(item); + if (!removed) { + System.err.println(name+" sorted remove failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterRemoveSortedTime = System.nanoTime(); + removeSortedTime += afterRemoveSortedTime - beforeRemoveSortedTime; + if (debug > 0) System.out.println(name+" sorted remove time = " + (removeSortedTime / sortedCount) + " ns"); + } + + if (!collection.isEmpty()) { + System.err.println(name+" sorted isEmpty() failed."); + handleError(input,collection); + return false; + } + if (collection.size()!=0) { + System.err.println(name+" sorted size() failed."); + handleError(input,collection); + return false; + } + } + + sortedCount++; + { // SORTED: Add in order (from index zero to length) and then remove in reverse (from index length-1 to zero) order + beforeMemory = 0L; + afterMemory = 0L; + beforeAddSortedTime = 0L; + afterAddSortedTime = 0L; + if (debugMemory) beforeMemory = DataStructuresTiming.getMemoryUse(); + if (debugTime) beforeAddSortedTime = System.nanoTime(); + for (int i = 0; i < sorted.length; i++) { + T item = sorted[i]; + boolean added = collection.add(item); + if (!added) { + System.err.println(name+" sorted add failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterAddSortedTime = System.nanoTime(); + addSortedTime += afterAddSortedTime - beforeAddSortedTime; + if (debug > 0) System.out.println(name+" sorted add time = " + (addSortedTime / sortedCount) + " ns"); + } + if (debugMemory) { + afterMemory = DataStructuresTiming.getMemoryUse(); + memory += afterMemory - beforeMemory; + if (debug > 0) System.out.println(name+" sorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); + } + + if (debug > 1) System.out.println(collection.toString()); + + beforeLookupTime = 0L; + afterLookupTime = 0L; + if (debugTime) beforeLookupTime = System.nanoTime(); + for (int i = 0; i < sorted.length; i++) { + T item = sorted[i]; + boolean contains = collection.contains(item); + if (!contains) { + System.err.println(name+" sorted contains failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterLookupTime = System.nanoTime(); + lookupTime += afterLookupTime - beforeLookupTime; + if (debug > 0) System.out.println(name+" sorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); + } + + beforeRemoveSortedTime = 0L; + afterRemoveSortedTime = 0L; + if (debugTime) beforeRemoveSortedTime = System.nanoTime(); + for (int i = sorted.length - 1; i >= 0; i--) { + T item = sorted[i]; + boolean removed = collection.remove(item); + if (!removed) { + System.err.println(name+" sorted remove failed."); + handleError(input,collection); + return false; + } + } + if (debugTime) { + afterRemoveSortedTime = System.nanoTime(); + removeSortedTime += afterRemoveSortedTime - beforeRemoveSortedTime; + if (debug > 0) System.out.println(name+" sorted remove time = " + (removeSortedTime / sortedCount) + " ns"); + } + + if (!collection.isEmpty()) { + System.err.println(name+" sorted isEmpty() failed."); + handleError(input,collection); + return false; + } + if (collection.size()!=0) { + System.err.println(name+" sorted size() failed."); + handleError(input,collection); + return false; + } + } + + // Throw away first test to remove JITing + if (!firstTimeThru) { + if (TEST_RESULTS[testIndex] == null) + TEST_RESULTS[testIndex] = new long[6]; + TEST_RESULTS[testIndex][0] += (addTime / unsortedCount); + TEST_RESULTS[testIndex][1] += (removeTime / unsortedCount); + TEST_RESULTS[testIndex][2] += (addSortedTime / sortedCount); + TEST_RESULTS[testIndex][3] += (removeSortedTime / sortedCount); + TEST_RESULTS[testIndex][4] += (lookupTime / (unsortedCount + sortedCount)); + TEST_RESULTS[testIndex][5] += (memory / (unsortedCount + sortedCount)); + } + + if (debug > 1) System.out.println(); + + return true; + } + + @SuppressWarnings("unchecked") + private static ,V> boolean testJavaMap(java.util.Map map, Class keyType, Class valueType, String name, Integer[] _unsorted, Integer[] _sorted, String input) { + // Make sure the map is empty + if (!map.isEmpty()) { + System.err.println(name+" initial isEmpty() failed."); + handleError(input,map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" initial size() failed."); + handleError(input,map); + return false; + } + + K[] kUnsorted = (K[]) Array.newInstance(keyType, _unsorted.length); + K[] kSorted = (K[]) Array.newInstance(keyType, _sorted.length); + V[] vUnsorted = (V[]) Array.newInstance(valueType, _unsorted.length); + V[] vSorted = (V[]) Array.newInstance(valueType, _sorted.length); + for (int i=0; i 1) System.out.println(name); + + // Throw away first test to remove JITing + if (!firstTimeThru) + TEST_NAMES[testIndex] = name; + + unsortedCount++; + { + beforeMemory = 0L; + afterMemory = 0L; + beforeAddTime = 0L; + afterAddTime = 0L; + if (debugMemory) beforeMemory = DataStructuresTiming.getMemoryUse(); + if (debugTime) beforeAddTime = System.nanoTime(); + for (int i = 0; i < kUnsorted.length; i++) { + K k = kUnsorted[i]; + V v = vUnsorted[i]; + map.put(k, v); + } + if (debugTime) { + afterAddTime = System.nanoTime(); + addTime += afterAddTime - beforeAddTime; + if (debug > 0) System.out.println(name+" unsorted add time = " + (addTime / unsortedCount) + " ns"); + } + if (debugMemory) { + afterMemory = DataStructuresTiming.getMemoryUse(); + memory += afterMemory - beforeMemory; + if (debug > 0) System.out.println(name+" unsorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); + } + + K invalidKey = (K) Utils.parseT(INVALID, keyType); + boolean contains = map.containsKey(invalidKey); + V removed = map.remove(invalidKey); + if (contains || (removed!=null)) { + System.err.println(name+" unsorted invalidity check. contains=" + contains + " removed=" + removed); + return false; + } + + if (debug > 1) System.out.println(map.toString()); + + beforeLookupTime = 0L; + afterLookupTime = 0L; + if (debugTime) beforeLookupTime = System.nanoTime(); + for (K k : kUnsorted) { + map.containsKey(k); + } + if (debugTime) { + afterLookupTime = System.nanoTime(); + lookupTime += afterLookupTime - beforeLookupTime; + if (debug > 0) System.out.println(name+" unsorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); + } + + if (debugTime) beforeRemoveTime = System.nanoTime(); + for (int i = 0; i < kUnsorted.length; i++) { + K k = kUnsorted[i]; + removed = map.remove(k); + if (removed==null) { + System.err.println(name+" unsorted invalidity check. removed=" + removed); + return false; + } + + } + if (debugTime) { + afterRemoveTime = System.nanoTime(); + removeTime += afterRemoveTime - beforeRemoveTime; + if (debug > 0) System.out.println(name+" unsorted remove time = " + (removeTime / unsortedCount) + " ns"); + } + + if (!map.isEmpty()) { + System.err.println(name+" unsorted isEmpty() failed."); + handleError(input,map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" unsorted size() failed."); + handleError(input,map); + return false; + } + } + + unsortedCount++; + { + beforeMemory = 0L; + afterMemory = 0L; + beforeAddTime = 0L; + afterAddTime = 0L; + if (debugMemory) beforeMemory = DataStructuresTiming.getMemoryUse(); + if (debugTime) beforeAddTime = System.nanoTime(); + for (int i = kUnsorted.length - 1; i >= 0; i--) { + K k = kUnsorted[i]; + V v = vUnsorted[i]; + map.put(k, v); + } + if (debugTime) { + afterAddTime = System.nanoTime(); + addTime += afterAddTime - beforeAddTime; + if (debug > 0) System.out.println(name+" unsorted add time = " + (addTime / unsortedCount) + " ns"); + } + if (debugMemory) { + afterMemory = DataStructuresTiming.getMemoryUse(); + memory += afterMemory - beforeMemory; + if (debug > 0) System.out.println(name+" unsorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); + } + + K invalidKey = (K) Utils.parseT(INVALID, keyType); + boolean contains = map.containsKey(invalidKey); + V removed = map.remove(invalidKey); + if (contains || (removed!=null)) { + System.err.println(name+" unsorted invalidity check. contains=" + contains + " removed=" + removed); + return false; + } + + if (debug > 1) System.out.println(map.toString()); + + beforeLookupTime = 0L; + afterLookupTime = 0L; + if (debugTime) beforeLookupTime = System.nanoTime(); + for (K k : kUnsorted) { + map.containsKey(k); + } + if (debugTime) { + afterLookupTime = System.nanoTime(); + lookupTime += afterLookupTime - beforeLookupTime; + if (debug > 0) System.out.println(name+" unsorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); + } + + beforeRemoveTime = 0L; + afterRemoveTime = 0L; + if (debugTime) beforeRemoveTime = System.nanoTime(); + for (int i = kUnsorted.length - 1; i >= 0; i--) { + K k = kUnsorted[i]; + removed = map.remove(k); + if (removed==null) { + System.err.println(name+" unsorted invalidity check. removed=" + removed); + return false; + } + } + if (debugTime) { + afterRemoveTime = System.nanoTime(); + removeTime += afterRemoveTime - beforeRemoveTime; + if (debug > 0) System.out.println(name+" unsorted remove time = " + (removeTime / unsortedCount) + " ns"); + } + + if (!map.isEmpty()) { + System.err.println(name+" unsorted isEmpty() failed."); + handleError(input,map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" unsorted size() failed."); + handleError(input,map); + return false; + } + } + + long addSortedTime = 0L; + long removeSortedTime = 0L; + + long beforeAddSortedTime = 0L; + long afterAddSortedTime = 0L; + + long beforeRemoveSortedTime = 0L; + long afterRemoveSortedTime = 0L; + + sortedCount++; + { // sorted + beforeMemory = 0L; + afterMemory = 0L; + beforeAddSortedTime = 0L; + afterAddSortedTime = 0L; + if (debugMemory) beforeMemory = DataStructuresTiming.getMemoryUse(); + if (debugTime) beforeAddSortedTime = System.nanoTime(); + for (int i = 0; i < kSorted.length; i++) { + K k = kSorted[i]; + V v = vSorted[i]; + map.put(k, v); + } + if (debugTime) { + afterAddSortedTime = System.nanoTime(); + addSortedTime += afterAddSortedTime - beforeAddSortedTime; + if (debug > 0) System.out.println(name+" sorted add time = " + (addSortedTime / sortedCount) + " ns"); + } + if (debugMemory) { + afterMemory = DataStructuresTiming.getMemoryUse(); + memory += afterMemory - beforeMemory; + if (debug > 0) System.out.println(name+" sorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); + } + + K invalidKey = (K) Utils.parseT(INVALID, keyType); + boolean contains = map.containsKey(invalidKey); + V removed = map.remove(invalidKey); + if (contains || (removed!=null)) { + System.err.println(name+" sorted invalidity check. contains=" + contains + " removed=" + removed); + return false; + } + + if (debug > 1) System.out.println(map.toString()); + + beforeLookupTime = 0L; + afterLookupTime = 0L; + if (debugTime) beforeLookupTime = System.nanoTime(); + for (K k : kSorted) { + map.containsKey(k); + } + if (debugTime) { + afterLookupTime = System.nanoTime(); + lookupTime += afterLookupTime - beforeLookupTime; + if (debug > 0) System.out.println(name+" sorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); + } + + beforeRemoveSortedTime = 0L; + afterRemoveSortedTime = 0L; + if (debugTime) beforeRemoveSortedTime = System.nanoTime(); + for (int i = 0; i < kSorted.length; i++) { + K k = kSorted[i]; + removed = map.remove(k); + if (removed==null) { + System.err.println(name+" unsorted invalidity check. removed=" + removed); + return false; + } + } + if (debugTime) { + afterRemoveSortedTime = System.nanoTime(); + removeSortedTime += afterRemoveSortedTime - beforeRemoveSortedTime; + if (debug > 0) System.out.println(name+" sorted remove time = " + (removeSortedTime / sortedCount) + " ns"); + } + + if (!map.isEmpty()) { + System.err.println(name+" sorted isEmpty() failed."); + handleError(input,map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" sorted size() failed."); + handleError(input,map); + return false; + } + } + + sortedCount++; + { // sorted + beforeMemory = 0L; + afterMemory = 0L; + beforeAddSortedTime = 0L; + afterAddSortedTime = 0L; + if (debugMemory) beforeMemory = DataStructuresTiming.getMemoryUse(); + if (debugTime) beforeAddSortedTime = System.nanoTime(); + for (int i = 0; i < kSorted.length; i++) { + K k = kSorted[i]; + V v = vSorted[i]; + map.put(k, v); + } + if (debugTime) { + afterAddSortedTime = System.nanoTime(); + addSortedTime += afterAddSortedTime - beforeAddSortedTime; + if (debug > 0) System.out.println(name+" sorted add time = " + (addSortedTime / sortedCount) + " ns"); + } + if (debugMemory) { + afterMemory = DataStructuresTiming.getMemoryUse(); + memory += afterMemory - beforeMemory; + if (debug > 0) System.out.println(name+" sorted memory use = " + (memory / (unsortedCount+sortedCount)) + " bytes"); + } + + K invalidKey = (K) Utils.parseT(INVALID, keyType); + boolean contains = map.containsKey(invalidKey); + V removed = map.remove(invalidKey); + if (contains || (removed!=null)) { + System.err.println(name+" sorted invalidity check. contains=" + contains + " removed=" + removed); + return false; + } + + if (debug > 1) System.out.println(map.toString()); + + beforeLookupTime = 0L; + afterLookupTime = 0L; + if (debugTime) beforeLookupTime = System.nanoTime(); + for (K k : kSorted) { + map.containsKey(k); + } + if (debugTime) { + afterLookupTime = System.nanoTime(); + lookupTime += afterLookupTime - beforeLookupTime; + if (debug > 0) System.out.println(name+" sorted lookup time = " + (lookupTime / (unsortedCount+sortedCount)) + " ns"); + } + + beforeRemoveSortedTime = 0L; + afterRemoveSortedTime = 0L; + if (debugTime) beforeRemoveSortedTime = System.nanoTime(); + for (int i = kSorted.length - 1; i >= 0; i--) { + K k = kSorted[i]; + removed = map.remove(k); + if (removed==null) { + System.err.println(name+" unsorted invalidity check. removed=" + removed); + return false; + } + } + if (debugTime) { + afterRemoveSortedTime = System.nanoTime(); + removeSortedTime += afterRemoveSortedTime - beforeRemoveSortedTime; + if (debug > 0) System.out.println(name+" sorted remove time = " + (removeSortedTime / sortedCount) + " ns"); + } + + if (!map.isEmpty()) { + System.err.println(name+" sorted isEmpty() failed."); + handleError(input,map); + return false; + } + if (map.size()!=0) { + System.err.println(name+" sorted size() failed."); + handleError(input,map); + return false; + } + + } + + // Throw away first test to remove JITing + if (!firstTimeThru) { + if (TEST_RESULTS[testIndex] == null) + TEST_RESULTS[testIndex] = new long[6]; + TEST_RESULTS[testIndex][0] += (addTime / unsortedCount); + TEST_RESULTS[testIndex][1] += (removeTime / unsortedCount); + TEST_RESULTS[testIndex][2] += (addSortedTime / sortedCount); + TEST_RESULTS[testIndex][3] += (removeSortedTime / sortedCount); + TEST_RESULTS[testIndex][4] += (lookupTime / (unsortedCount + sortedCount)); + TEST_RESULTS[testIndex][5] += (memory / (unsortedCount + sortedCount)); + } + + if (debug > 1) System.out.println(); + + return true; + } + + private static final String getTestResults(int number, String[] names, long[][] results) { + StringBuilder resultsBuilder = new StringBuilder(); + String format = "%-35s %-10s %-15s %-15s %-25s %-15s %-15s\n"; + Formatter formatter = new Formatter(resultsBuilder, Locale.US); + formatter.format(format, "Data Structure ("+ARRAY_SIZE+" items)", "Add time", "Remove time", "Sorted add time", "Sorted remove time", "Lookup time", "Size"); + + double KB = 1000; + double MB = 1000 * KB; + + double MILLIS = 1000000; + double SECOND = 1000; + double MINUTES = 60 * SECOND; + + for (int i=0; i MINUTES) { + addTime /= MINUTES; + addTimeString = FORMAT.format(addTime) + " m"; + } else if (addTime > SECOND) { + addTime /= SECOND; + addTimeString = FORMAT.format(addTime) + " s"; + } else { + addTimeString = FORMAT.format(addTime) + " ms"; + } + + double removeTime = result[1] / MILLIS; + removeTime /= number; + String removeTimeString = null; + if (removeTime > MINUTES) { + removeTime /= MINUTES; + removeTimeString = FORMAT.format(removeTime) + " m"; + } else if (removeTime > SECOND) { + removeTime /= SECOND; + removeTimeString = FORMAT.format(removeTime) + " s"; + } else { + removeTimeString = FORMAT.format(removeTime) + " ms"; + } + + // sorted + double addSortedTime = result[2] / MILLIS; + addSortedTime /= number; + String sortedAddTimeString = null; + if (addSortedTime > MINUTES) { + addSortedTime /= MINUTES; + sortedAddTimeString = FORMAT.format(addSortedTime) + " m"; + } else if (addSortedTime > SECOND) { + addSortedTime /= SECOND; + sortedAddTimeString = FORMAT.format(addSortedTime) + " s"; + } else { + sortedAddTimeString = FORMAT.format(addSortedTime) + " ms"; + } + + double removeSortedTime = result[3] / MILLIS; + removeSortedTime /= number; + String sortedRemoveTimeString = null; + if (removeSortedTime > MINUTES) { + removeSortedTime /= MINUTES; + sortedRemoveTimeString = FORMAT.format(removeSortedTime) + " m"; + } else if (removeSortedTime > SECOND) { + removeSortedTime /= SECOND; + sortedRemoveTimeString = FORMAT.format(removeSortedTime) + " s"; + } else { + sortedRemoveTimeString = FORMAT.format(removeSortedTime) + " ms"; + } + + double lookupTime = result[4] / MILLIS; + lookupTime /= number; + String lookupTimeString = null; + if (lookupTime > MINUTES) { + lookupTime /= MINUTES; + lookupTimeString = FORMAT.format(lookupTime) + " m"; + } else if (lookupTime > SECOND) { + lookupTime /= SECOND; + lookupTimeString = FORMAT.format(lookupTime) + " s"; + } else { + lookupTimeString = FORMAT.format(lookupTime) + " ms"; + } + + double size = result[5]; + size /= number; + String sizeString = null; + if (size > MB) { + size = size / MB; + sizeString = FORMAT.format(size) + " MB"; + } else if (size > KB) { + size = size / KB; + sizeString = FORMAT.format(size) + " KB"; + } else { + sizeString = FORMAT.format(size) + " Bytes"; + } + + formatter.format(format, name, addTimeString, removeTimeString, sortedAddTimeString, sortedRemoveTimeString, lookupTimeString, sizeString); + } + } + formatter.close(); + + return resultsBuilder.toString(); + } + + static final long getMemoryUse() { + putOutTheGarbage(); + long totalMemory = Runtime.getRuntime().totalMemory(); + + putOutTheGarbage(); + long freeMemory = Runtime.getRuntime().freeMemory(); + + return (totalMemory - freeMemory); + } + + private static final long SLEEP_INTERVAL = 100; + + static final void putOutTheGarbage() { + putOutTheGarbage(SLEEP_INTERVAL); + } + + static final void putOutTheGarbage(long sleep) { + collectGarbage(sleep); + collectGarbage(sleep); + collectGarbage(sleep); + } + + private static final void collectGarbage(long sleep) { + try { + System.gc(); + System.gc(); + System.gc(); + Thread.sleep(sleep); + System.runFinalization(); + Thread.sleep(sleep); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } +} diff --git a/test/com/jwetherell/algorithms/data_structures/timing/SpatialDataStructuresTiming.java b/test/com/jwetherell/algorithms/data_structures/timing/SpatialDataStructuresTiming.java new file mode 100644 index 00000000..1fc53366 --- /dev/null +++ b/test/com/jwetherell/algorithms/data_structures/timing/SpatialDataStructuresTiming.java @@ -0,0 +1,333 @@ +package com.jwetherell.algorithms.data_structures.timing; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Formatter; +import java.util.List; +import java.util.Locale; +import java.util.Random; + +import junit.framework.Assert; + +import com.jwetherell.algorithms.data_structures.KdTree; +import com.jwetherell.algorithms.data_structures.QuadTree; + +public class SpatialDataStructuresTiming { + + private static final int NUMBER_OF_TESTS = 3; // There will always be NUMBER_OF_TESTS+1 runs since the first round is thrown away (JITing) + private static final Random RANDOM = new Random(); + private static final int ARRAY_SIZE = 1024*10; + private static final int RANDOM_SIZE = 1000 * ARRAY_SIZE; + + private static final DecimalFormat FORMAT = new DecimalFormat("0.##"); + private static final int TESTS = 3; + private static final String[] testNames = new String[TESTS]; // Array to hold the test names + private static final long[][] testResults = new long[TESTS][]; // Array to hold the test results + + private static int[][] data = null; + private static String stringifiedData = null; + + public static void main(String[] args) { + System.out.println("Starting tests."); + for (int i=0; i prTree = new QuadTree.PointRegionQuadTree(0,0,RANDOM_SIZE,RANDOM_SIZE); + testQuadTree("PointRegionQuadTree", round, test++, 100, data, prTree); + DataStructuresTiming.putOutTheGarbage(); + System.out.println(); + + QuadTree aaTree = new QuadTree.MxCifQuadTree(0,0,RANDOM_SIZE,RANDOM_SIZE,10000,10000); + testQuadTree("MxCifQuadTree", round, test++, 100, data, aaTree); + DataStructuresTiming.putOutTheGarbage(); + System.out.println(); + + if (round!=0) { + // Skip first round to avoid JIT + System.out.println(getTestResults(round, testNames, testResults)); + } + } + + private static boolean testKdTree(String name, int testRound, int testNum, int[][] unsorteds) { + if (testRound != 0) { + // Skip first round to avoid JIT + testNames[testNum] = name; + if (testResults[testNum] == null) + testResults[testNum] = new long[5]; + } + + int test = 0; + + List points = new ArrayList(ARRAY_SIZE); + for (int i=0; i tree = new KdTree(points, 2); // 2 since we only care about two dimensions (x,y) + long afterAddTime = System.nanoTime(); + long afterMemory = DataStructuresTiming.getMemoryUse(); + + long memory = afterMemory - beforeMemory; + if (testRound != 0) + testResults[testNum][test++] += memory; + System.out.println(name+" memory use = " + (memory / ARRAY_SIZE) + " bytes"); + long addTime = afterAddTime - beforeAddTime; + if (testRound != 0) + testResults[testNum][test++] += addTime; + System.out.println(name + " add time = " + (addTime / ARRAY_SIZE) + " ns"); + + // contains + long beforeContainsTime = System.nanoTime(); + for (KdTree.XYZPoint p : points) { + boolean r = tree.contains(p); + assertTrue("Point not found.", p, tree, r==true); + } + long afterContainsTime = System.nanoTime(); + + long containsTime = afterContainsTime - beforeContainsTime; + if (testRound != 0) + testResults[testNum][test++] += containsTime; + System.out.println(name + " contains time = " + (containsTime / ARRAY_SIZE) + " ns"); + + // nearest neighbor + long beforeNnTime = System.nanoTime(); + for (KdTree.XYZPoint p : points) { + Collection c = tree.nearestNeighbourSearch(4, p); + assertTrue("nearest neighbor didn't find anyone.", p, tree, c.size()>0); + } + long afterNnTime = System.nanoTime(); + + long nnTime = afterNnTime - beforeNnTime; + if (testRound != 0) + testResults[testNum][test++] += nnTime; + System.out.println(name + " nearest neighbor time = " + (nnTime / ARRAY_SIZE) + " ns"); + + // remove + long beforeRemovesTime = System.nanoTime(); + for (KdTree.XYZPoint p : points) { + boolean r = tree.remove(p); + assertTrue("Point not removed.", p, tree, r==true); + } + long afterRemovesTime = System.nanoTime(); + + long removesTime = afterRemovesTime - beforeRemovesTime; + if (testRound != 0) + testResults[testNum][test++] += removesTime; + System.out.println(name + " removes time = " + (removesTime / ARRAY_SIZE) + " ns"); + + return true; + } + + private static boolean testQuadTree(String name, int testRound, int testNum, int range, int[][] unsorteds, QuadTree tree) { + if (testRound != 0) { + // Skip first round to avoid JIT + testNames[testNum] = name; + if (testResults[testNum] == null) + testResults[testNum] = new long[5]; + } + + int test = 0; + + List points = new ArrayList(ARRAY_SIZE); + for (int i=0; i l = tree.queryRange(p.getX(), p.getY(), 1, 1); // looking for a single point + assertTrue("Point not found.", p, tree, l.size()>0); + } + long afterContainsTime = System.nanoTime(); + + long containsTime = afterContainsTime - beforeContainsTime; + if (testRound != 0) + testResults[testNum][test++] += containsTime; + System.out.println(name + " contains time = " + (containsTime / ARRAY_SIZE) + " ns"); + + // query range + long beforeQrTime = System.nanoTime(); + for (QuadTree.XYPoint p : points) { + Collection l = tree.queryRange(p.getX(), p.getY(), range, range); + assertTrue("Range query returned no values.", p, tree, l.size()>0); + } + long afterQrTime = System.nanoTime(); + + long qrTime = afterQrTime - beforeQrTime; + if (testRound != 0) + testResults[testNum][test++] += qrTime; + System.out.println(name + " query range time = " + (qrTime / ARRAY_SIZE) + " ns"); + + // remove + long beforeRemovesTime = System.nanoTime(); + for (QuadTree.XYPoint p : points) { + boolean r = tree.remove(p.getX(), p.getY()); + assertTrue("Point not removed.", p, tree, r==true); + } + long afterRemovesTime = System.nanoTime(); + + long removesTime = afterRemovesTime - beforeRemovesTime; + if (testRound != 0) + testResults[testNum][test++] += removesTime; + System.out.println(name + " removes time = " + (removesTime / ARRAY_SIZE) + " ns"); + + return true; + } + + private static final String getTestResults(int number, String[] names, long[][] results) { + StringBuilder resultsBuilder = new StringBuilder(); + String format = "%-35s %-10s %-15s %-15s %-15s %-15s\n"; + Formatter formatter = new Formatter(resultsBuilder, Locale.US); + formatter.format(format, "Data Structure ("+ARRAY_SIZE+" items)", "Add time", "Remove time", "Lookup time", "Query", "Size"); + + double KB = 1000; + double MB = 1000 * KB; + + double MILLIS = 1000000; + double SECOND = 1000; + double MINUTES = 60 * SECOND; + + for (int i=0; i MB) { + size = size / MB; + sizeString = FORMAT.format(size) + " MB"; + } else if (size > KB) { + size = size / KB; + sizeString = FORMAT.format(size) + " KB"; + } else { + sizeString = FORMAT.format(size) + " Bytes"; + } + + double addTime = result[1] / MILLIS; + addTime /= number; + String addTimeString = null; + if (addTime > MINUTES) { + addTime /= MINUTES; + addTimeString = FORMAT.format(addTime) + " m"; + } else if (addTime > SECOND) { + addTime /= SECOND; + addTimeString = FORMAT.format(addTime) + " s"; + } else { + addTimeString = FORMAT.format(addTime) + " ms"; + } + + double lookupTime = result[2] / MILLIS; + lookupTime /= number; + String lookupTimeString = null; + if (lookupTime > MINUTES) { + lookupTime /= MINUTES; + lookupTimeString = FORMAT.format(lookupTime) + " m"; + } else if (lookupTime > SECOND) { + lookupTime /= SECOND; + lookupTimeString = FORMAT.format(lookupTime) + " s"; + } else { + lookupTimeString = FORMAT.format(lookupTime) + " ms"; + } + + double addQueryTime = result[3] / MILLIS; + addQueryTime /= number; + String queryTimeString = null; + if (addQueryTime > MINUTES) { + addQueryTime /= MINUTES; + queryTimeString = FORMAT.format(addQueryTime) + " m"; + } else if (addQueryTime > SECOND) { + addQueryTime /= SECOND; + queryTimeString = FORMAT.format(addQueryTime) + " s"; + } else { + queryTimeString = FORMAT.format(addQueryTime) + " ms"; + } + + double removeTime = result[4] / MILLIS; + removeTime /= number; + String removeTimeString = null; + if (removeTime > MINUTES) { + removeTime /= MINUTES; + removeTimeString = FORMAT.format(removeTime) + " m"; + } else if (removeTime > SECOND) { + removeTime /= SECOND; + removeTimeString = FORMAT.format(removeTime) + " s"; + } else { + removeTimeString = FORMAT.format(removeTime) + " ms"; + } + + formatter.format(format, name, addTimeString, removeTimeString, lookupTimeString, queryTimeString, sizeString); + } + } + formatter.close(); + + return resultsBuilder.toString(); + } + + // Assertion which won't call toString on the tree unless the assertion fails + private static final

void assertTrue(String msg, P p, KdTree

obj, boolean isTrue) { + String toString = ""; + if (isTrue==false) + toString = p.toString()+"\n"+"data=["+stringifiedData+"]\n"+obj.toString(); + Assert.assertTrue(msg+toString, isTrue); + } + + // Assertion which won't call toString on the tree unless the assertion fails + private static final

void assertTrue(String msg, P p, QuadTree obj, boolean isTrue) { + String toString = ""; + if (isTrue==false) + toString = p.toString()+"\n"+"data=["+stringifiedData+"]\n"+obj.toString(); + Assert.assertTrue(msg+toString, isTrue); + } +} diff --git a/test/com/jwetherell/algorithms/graph/test/BreadthFirstTraversalTest.java b/test/com/jwetherell/algorithms/graph/test/BreadthFirstTraversalTest.java new file mode 100644 index 00000000..057ec566 --- /dev/null +++ b/test/com/jwetherell/algorithms/graph/test/BreadthFirstTraversalTest.java @@ -0,0 +1,89 @@ +package com.jwetherell.algorithms.graph.test; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.graph.BreadthFirstTraversal; + +public class BreadthFirstTraversalTest { + + private static final byte[][] adjacencyMatrix = new byte[4][4]; + static { + // v0 + adjacencyMatrix[0][1] = 1; + adjacencyMatrix[0][2] = 1; + // v1 + adjacencyMatrix[1][2] = 1; + // v2 + adjacencyMatrix[2][0] = 1; + adjacencyMatrix[2][3] = 1; + // v3 + adjacencyMatrix[3][3] = 1; + } + + @Test + public void test0() { + final int[] result = BreadthFirstTraversal.breadthFirstTraversal(4, adjacencyMatrix, 2); + Assert.assertTrue(result[0]==2); + Assert.assertTrue(result[1]==0); + Assert.assertTrue(result[2]==3); + Assert.assertTrue(result[3]==1); + } + + @Test + public void test1() { + final int[] result = BreadthFirstTraversal.breadthFirstTraversal(4, adjacencyMatrix, 0); + Assert.assertTrue(result[0]==0); + Assert.assertTrue(result[1]==1); + Assert.assertTrue(result[2]==2); + Assert.assertTrue(result[3]==3); + } + + private static final List> vertices = new ArrayList>(); + private static final List> edges = new ArrayList>(); + + private static final Graph.Vertex v0 = new Graph.Vertex(0); + private static final Graph.Vertex v1 = new Graph.Vertex(1); + private static final Graph.Vertex v2 = new Graph.Vertex(2); + private static final Graph.Vertex v3 = new Graph.Vertex(3); + + static { + vertices.add(v0); + vertices.add(v1); + vertices.add(v2); + vertices.add(v3); + + edges.add(new Graph.Edge(0, v0, v1)); + edges.add(new Graph.Edge(0, v0, v2)); + edges.add(new Graph.Edge(0, v1, v2)); + edges.add(new Graph.Edge(0, v2, v0)); + edges.add(new Graph.Edge(0, v2, v3)); + edges.add(new Graph.Edge(0, v3, v3)); + } + + private static final Graph graph = new Graph(Graph.TYPE.DIRECTED, vertices, edges); + + @Test + public void test2() { + final Graph.Vertex[] result = BreadthFirstTraversal.breadthFirstTraversal(graph, v2); + Assert.assertTrue(result[0].getValue()==2); + Assert.assertTrue(result[1].getValue()==0); + Assert.assertTrue(result[2].getValue()==3); + Assert.assertTrue(result[3].getValue()==1); + } + + @Test + public void test3() { + final Graph.Vertex[] result = BreadthFirstTraversal.breadthFirstTraversal(graph, v0); + Assert.assertTrue(result[0].getValue()==0); + Assert.assertTrue(result[1].getValue()==1); + Assert.assertTrue(result[2].getValue()==2); + Assert.assertTrue(result[3].getValue()==3); + } +} + diff --git a/test/com/jwetherell/algorithms/graph/test/DepthFirstTraversalTest.java b/test/com/jwetherell/algorithms/graph/test/DepthFirstTraversalTest.java new file mode 100644 index 00000000..48e26c89 --- /dev/null +++ b/test/com/jwetherell/algorithms/graph/test/DepthFirstTraversalTest.java @@ -0,0 +1,89 @@ +package com.jwetherell.algorithms.graph.test; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.graph.DepthFirstTraversal; + +public class DepthFirstTraversalTest { + + private static final byte[][] adjacencyMatrix = new byte[4][4]; + static { + // v0 + adjacencyMatrix[0][1] = 1; + adjacencyMatrix[0][2] = 1; + // v1 + adjacencyMatrix[1][2] = 1; + // v2 + adjacencyMatrix[2][0] = 1; + adjacencyMatrix[2][3] = 1; + // v3 + adjacencyMatrix[3][3] = 1; + } + + @Test + public void test0() { + final int[] result = DepthFirstTraversal.depthFirstTraversal(4, adjacencyMatrix, 2); + Assert.assertTrue(result[0]==2); + Assert.assertTrue(result[1]==0); + Assert.assertTrue(result[2]==1); + Assert.assertTrue(result[3]==3); + } + + @Test + public void test1() { + final int[] result = DepthFirstTraversal.depthFirstTraversal(4, adjacencyMatrix, 0); + Assert.assertTrue(result[0]==0); + Assert.assertTrue(result[1]==1); + Assert.assertTrue(result[2]==2); + Assert.assertTrue(result[3]==3); + } + + private static final List> vertices = new ArrayList>(); + private static final List> edges = new ArrayList>(); + + private static final Graph.Vertex v0 = new Graph.Vertex(0); + private static final Graph.Vertex v1 = new Graph.Vertex(1); + private static final Graph.Vertex v2 = new Graph.Vertex(2); + private static final Graph.Vertex v3 = new Graph.Vertex(3); + + static { + vertices.add(v0); + vertices.add(v1); + vertices.add(v2); + vertices.add(v3); + + edges.add(new Graph.Edge(0, v0, v1)); + edges.add(new Graph.Edge(0, v0, v2)); + edges.add(new Graph.Edge(0, v1, v2)); + edges.add(new Graph.Edge(0, v2, v0)); + edges.add(new Graph.Edge(0, v2, v3)); + edges.add(new Graph.Edge(0, v3, v3)); + } + + private static final Graph graph = new Graph(Graph.TYPE.DIRECTED, vertices, edges); + + @Test + public void test2() { + final Graph.Vertex[] result = DepthFirstTraversal.depthFirstTraversal(graph, v2); + Assert.assertTrue(result[0].getValue()==2); + Assert.assertTrue(result[1].getValue()==0); + Assert.assertTrue(result[2].getValue()==1); + Assert.assertTrue(result[3].getValue()==3); + } + + @Test + public void test3() { + final Graph.Vertex[] result = DepthFirstTraversal.depthFirstTraversal(graph, v0); + Assert.assertTrue(result[0].getValue()==0); + Assert.assertTrue(result[1].getValue()==1); + Assert.assertTrue(result[2].getValue()==2); + Assert.assertTrue(result[3].getValue()==3); + } +} + diff --git a/test/com/jwetherell/algorithms/graph/test/EdmondsKarpTest.java b/test/com/jwetherell/algorithms/graph/test/EdmondsKarpTest.java new file mode 100644 index 00000000..f44fff49 --- /dev/null +++ b/test/com/jwetherell/algorithms/graph/test/EdmondsKarpTest.java @@ -0,0 +1,87 @@ +package com.jwetherell.algorithms.graph.test; + +import com.jwetherell.algorithms.graph.EdmondsKarp; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + + +/** + * Created by mateusz on 27.02.17. + */ +public class EdmondsKarpTest { + + @Test + public void Test1() { + int A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6; + EdmondsKarp ek = new EdmondsKarp(7, 11); + ek.addEdge(A, D, 3); + ek.addEdge(D, F, 6); + ek.addEdge(A, B, 3); + ek.addEdge(E, B, 1); + ek.addEdge(E, G, 1); + ek.addEdge(F, G, 9); + ek.addEdge(D, E, 2); + ek.addEdge(B, C, 4); + ek.addEdge(C, A, 3); + ek.addEdge(C, D, 1); + ek.addEdge(C, E, 2); + assertTrue(ek.getMaxFlow(A, G) == 5); + } + + @Test + public void Test2() { + EdmondsKarp ek = new EdmondsKarp(2, 1); + ek.addEdge(1, 0, 10); + assertTrue(ek.getMaxFlow(0, 1) == 0); + assertTrue(ek.getMaxFlow(1, 0) == 10); + } + + @Test + public void Test3() { + EdmondsKarp ek = new EdmondsKarp(4, 5); + ek.addEdge(0, 1, 1000); + ek.addEdge(0, 2, 1000); + ek.addEdge(1, 3, 1000); + ek.addEdge(2, 3, 1000); + ek.addEdge(1, 2, 1); + assertTrue(ek.getMaxFlow(0, 3) == 2000); + } + + @Test + public void Test4() { + EdmondsKarp ek = new EdmondsKarp(6, 8); + ek.addEdge(0, 1, 3); + ek.addEdge(0, 3, 3); + ek.addEdge(1, 3, 2); + ek.addEdge(1, 2, 3); + ek.addEdge(3, 4, 2); + ek.addEdge(4, 5, 3); + ek.addEdge(2, 4, 4); + ek.addEdge(2, 5, 2); + assertTrue(ek.getMaxFlow(0, 5) == 5); + } +/* + public static void main(String[] a) { + int A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6; + EdmondsKarp ek = new EdmondsKarp(7, 11); + ek.AddEdge(A, D, 3); + ek.AddEdge(D, F, 6); + ek.AddEdge(A, B, 3); + ek.AddEdge(E, B, 1); + ek.AddEdge(E, G, 1); + ek.AddEdge(F, G, 9); + ek.AddEdge(D, E, 2); + ek.AddEdge(B, C, 4); + ek.AddEdge(C, A, 3); + ek.AddEdge(C, D, 1); + ek.AddEdge(C, E, 2); + System.out.println(ek.GetMaxFlow(0, 6)); + EdmondsKarp ek2 = new EdmondsKarp(2, 1); + ek2.AddEdge(1, 0, 10); + System.out.println(ek2.GetMaxFlow(0, 1)); + System.out.println(ek2.GetMaxFlow(1, 0)); + } +*/ +} diff --git a/test/com/jwetherell/algorithms/graph/test/Graphs.java b/test/com/jwetherell/algorithms/graph/test/Graphs.java new file mode 100644 index 00000000..6a17dcd0 --- /dev/null +++ b/test/com/jwetherell/algorithms/graph/test/Graphs.java @@ -0,0 +1,1009 @@ +package com.jwetherell.algorithms.graph.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.jwetherell.algorithms.graph.*; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.data_structures.Graph.Edge; +import com.jwetherell.algorithms.data_structures.Graph.TYPE; +import com.jwetherell.algorithms.data_structures.Graph.Vertex; + +public class Graphs { + + // Undirected + private static class UndirectedGraph { + final List> verticies = new ArrayList>(); + final Graph.Vertex v1 = new Graph.Vertex(1); + final Graph.Vertex v2 = new Graph.Vertex(2); + final Graph.Vertex v3 = new Graph.Vertex(3); + final Graph.Vertex v4 = new Graph.Vertex(4); + final Graph.Vertex v5 = new Graph.Vertex(5); + final Graph.Vertex v6 = new Graph.Vertex(6); + final Graph.Vertex v7 = new Graph.Vertex(7); + final Graph.Vertex v8 = new Graph.Vertex(8); + { + verticies.add(v1); + verticies.add(v2); + verticies.add(v3); + verticies.add(v4); + verticies.add(v5); + verticies.add(v6); + verticies.add(v7); + verticies.add(v8); + } + + final List> edges = new ArrayList>(); + final Graph.Edge e1_2 = new Graph.Edge(7, v1, v2); + final Graph.Edge e1_3 = new Graph.Edge(9, v1, v3); + final Graph.Edge e1_6 = new Graph.Edge(14, v1, v6); + final Graph.Edge e2_3 = new Graph.Edge(10, v2, v3); + final Graph.Edge e2_4 = new Graph.Edge(15, v2, v4); + final Graph.Edge e3_4 = new Graph.Edge(11, v3, v4); + final Graph.Edge e3_6 = new Graph.Edge(2, v3, v6); + final Graph.Edge e5_6 = new Graph.Edge(9, v5, v6); + final Graph.Edge e4_5 = new Graph.Edge(6, v4, v5); + final Graph.Edge e1_7 = new Graph.Edge(1, v1, v7); + final Graph.Edge e1_8 = new Graph.Edge(1, v1, v8); + { + edges.add(e1_2); + edges.add(e1_3); + edges.add(e1_6); + edges.add(e2_3); + edges.add(e2_4); + edges.add(e3_4); + edges.add(e3_6); + edges.add(e5_6); + edges.add(e4_5); + edges.add(e1_7); + edges.add(e1_8); + } + + final Graph graph = new Graph(verticies, edges); + } + + // Directed + private static class DirectedGraph { + final List> verticies = new ArrayList>(); + final Graph.Vertex v1 = new Graph.Vertex(1); + final Graph.Vertex v2 = new Graph.Vertex(2); + final Graph.Vertex v3 = new Graph.Vertex(3); + final Graph.Vertex v4 = new Graph.Vertex(4); + final Graph.Vertex v5 = new Graph.Vertex(5); + final Graph.Vertex v6 = new Graph.Vertex(6); + final Graph.Vertex v7 = new Graph.Vertex(7); + final Graph.Vertex v8 = new Graph.Vertex(8); + { + verticies.add(v1); + verticies.add(v2); + verticies.add(v3); + verticies.add(v4); + verticies.add(v5); + verticies.add(v6); + verticies.add(v7); + verticies.add(v8); + } + + final List> edges = new ArrayList>(); + final Graph.Edge e1_2 = new Graph.Edge(7, v1, v2); + final Graph.Edge e1_3 = new Graph.Edge(9, v1, v3); + final Graph.Edge e1_6 = new Graph.Edge(14, v1, v6); + final Graph.Edge e2_3 = new Graph.Edge(10, v2, v3); + final Graph.Edge e2_4 = new Graph.Edge(15, v2, v4); + final Graph.Edge e3_4 = new Graph.Edge(11, v3, v4); + final Graph.Edge e3_6 = new Graph.Edge(2, v3, v6); + final Graph.Edge e6_5 = new Graph.Edge(9, v6, v5); + final Graph.Edge e6_8 = new Graph.Edge(14, v6, v8); + final Graph.Edge e4_5 = new Graph.Edge(6, v4, v5); + final Graph.Edge e4_7 = new Graph.Edge(16, v4, v7); + final Graph.Edge e1_8 = new Graph.Edge(30, v1, v8); + { + edges.add(e1_2); + edges.add(e1_3); + edges.add(e1_6); + edges.add(e2_3); + edges.add(e2_4); + edges.add(e3_4); + edges.add(e3_6); + edges.add(e6_5); + edges.add(e6_8); + edges.add(e4_5); + edges.add(e4_7); + edges.add(e1_8); + } + + final Graph graph = new Graph(Graph.TYPE.DIRECTED, verticies, edges); + } + + // Directed with negative weights + private static class DirectedWithNegativeWeights { + final List> verticies = new ArrayList>(); + final Graph.Vertex v1 = new Graph.Vertex(1); + final Graph.Vertex v2 = new Graph.Vertex(2); + final Graph.Vertex v3 = new Graph.Vertex(3); + final Graph.Vertex v4 = new Graph.Vertex(4); + { + verticies.add(v1); + verticies.add(v2); + verticies.add(v3); + verticies.add(v4); + } + + final List> edges = new ArrayList>(); + final Graph.Edge e1_4 = new Graph.Edge(2, v1, v4); // w->z + final Graph.Edge e2_1 = new Graph.Edge(6, v2, v1); // x->w + final Graph.Edge e2_3 = new Graph.Edge(3, v2, v3); // x->y + final Graph.Edge e3_1 = new Graph.Edge(4, v3, v1); // y->w + final Graph.Edge e3_4 = new Graph.Edge(5, v3, v4); // y->z + final Graph.Edge e4_2 = new Graph.Edge(-7, v4, v2); // z->x + final Graph.Edge e4_3 = new Graph.Edge(-3, v4, v3); // z->y + { + edges.add(e1_4); + edges.add(e2_1); + edges.add(e2_3); + edges.add(e3_1); + edges.add(e3_4); + edges.add(e4_2); + edges.add(e4_3); + } + + final Graph graph = new Graph(Graph.TYPE.DIRECTED, verticies, edges); + } + + // Ideal undirected path + private Map, Graph.CostPathPair> getIdealUndirectedPath(UndirectedGraph undirected) { + final HashMap, Graph.CostPathPair> idealUndirectedPath = new HashMap, Graph.CostPathPair>(); + { + final int cost = 11; + final List> list = new ArrayList>(); + list.add(undirected.e1_3); + list.add(undirected.e3_6); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealUndirectedPath.put(undirected.v6, path); + } + { + final int cost = 20; + final List> list = new ArrayList>(); + list.add(undirected.e1_3); + list.add(undirected.e3_6); + list.add(new Graph.Edge(9, undirected.v6, undirected.v5)); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealUndirectedPath.put(undirected.v5, path); + } + { + final int cost = 9; + final List> list = new ArrayList>(); + list.add(undirected.e1_3); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealUndirectedPath.put(undirected.v3, path); + } + { + final int cost = 20; + final List> list = new ArrayList>(); + list.add(undirected.e1_3); + list.add(undirected.e3_4); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealUndirectedPath.put(undirected.v4, path); + } + { + final int cost = 7; + final List> list = new ArrayList>(); + list.add(undirected.e1_2); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealUndirectedPath.put(undirected.v2, path); + } + { + final int cost = 0; + final List> list = new ArrayList>(); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealUndirectedPath.put(undirected.v1, path); + } + { + final int cost = 1; + final List> list = new ArrayList>(); + list.add(undirected.e1_7); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealUndirectedPath.put(undirected.v7, path); + } + { + final int cost = 1; + final List> list = new ArrayList>(); + list.add(undirected.e1_8); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealUndirectedPath.put(undirected.v8, path); + } + return idealUndirectedPath; + } + + // Ideal undirected PathPair + private Graph.CostPathPair getIdealUndirectedPathPair(UndirectedGraph undirected) { + final int cost = 20; + final List> list = new ArrayList>(); + list.add(undirected.e1_3); + list.add(undirected.e3_6); + list.add(new Graph.Edge(9, undirected.v6, undirected.v5)); + return (new Graph.CostPathPair(cost, list)); + } + + // Ideal directed path + private Map, Graph.CostPathPair> getIdealDirectedPath(DirectedGraph directed) { + final Map, Graph.CostPathPair> idealDirectedPath = new HashMap, Graph.CostPathPair>(); + { + final int cost = 11; + final List> list = new ArrayList>(); + list.add(directed.e1_3); + list.add(directed.e3_6); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedPath.put(directed.v6, path); + } + { + final int cost = 20; + final List> list = new ArrayList>(); + list.add(directed.e1_3); + list.add(directed.e3_6); + list.add(new Graph.Edge(9, directed.v6, directed.v5)); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedPath.put(directed.v5, path); + } + { + final int cost = 36; + final List> list = new ArrayList>(); + list.add(directed.e1_3); + list.add(directed.e3_4); + list.add(directed.e4_7); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedPath.put(directed.v7, path); + } + { + final int cost = 9; + final List> list = new ArrayList>(); + list.add(directed.e1_3); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedPath.put(directed.v3, path); + } + { + final int cost = 20; + final List> list = new ArrayList>(); + list.add(directed.e1_3); + list.add(directed.e3_4); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedPath.put(directed.v4, path); + } + { + final int cost = 7; + final List> list = new ArrayList>(); + list.add(directed.e1_2); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedPath.put(directed.v2, path); + } + { + final int cost = 0; + final List> list = new ArrayList>(); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedPath.put(directed.v1, path); + } + { + final int cost = 25; + final List> list = new ArrayList>(); + list.add(directed.e1_3); + list.add(directed.e3_6); + list.add(directed.e6_8); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedPath.put(directed.v8, path); + } + return idealDirectedPath; + } + + // Ideal directed Path Pair + private Graph.CostPathPair getIdealPathPair(DirectedGraph directed) { + final int cost = 20; + final List> list = new ArrayList>(); + list.add(directed.e1_3); + list.add(directed.e3_6); + list.add(new Graph.Edge(9, directed.v6, directed.v5)); + return (new Graph.CostPathPair(cost, list)); + } + + // Ideal directed with negative weight path + private Map, Graph.CostPathPair> getIdealDirectedNegWeight(DirectedWithNegativeWeights directedWithNegWeights) { + final Map, Graph.CostPathPair> idealDirectedNegWeight = new HashMap, Graph.CostPathPair>(); + { + final int cost = -2; + final List> list = new ArrayList>(); + list.add(directedWithNegWeights.e1_4); + list.add(directedWithNegWeights.e4_2); + list.add(directedWithNegWeights.e2_3); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedNegWeight.put(directedWithNegWeights.v3, path); + } + { + final int cost = 2; + final List> list = new ArrayList>(); + list.add(directedWithNegWeights.e1_4); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedNegWeight.put(directedWithNegWeights.v4, path); + } + { + final int cost = -5; + final List> list = new ArrayList>(); + list.add(directedWithNegWeights.e1_4); + list.add(directedWithNegWeights.e4_2); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedNegWeight.put(directedWithNegWeights.v2, path); + } + { + final int cost = 0; + final List> list = new ArrayList>(); + final Graph.CostPathPair path = new Graph.CostPathPair(cost, list); + idealDirectedNegWeight.put(directedWithNegWeights.v1, path); + } + return idealDirectedNegWeight; + } + + // Ideal pair + private Graph.CostPathPair getIdealDirectedWithNegWeightsPathPair(DirectedWithNegativeWeights directedWithNegWeights) { + final int cost = -2; + final List> list = new ArrayList>(); + list.add(directedWithNegWeights.e1_4); + list.add(directedWithNegWeights.e4_2); + list.add(directedWithNegWeights.e2_3); + return (new Graph.CostPathPair(cost, list)); + } + + @Test + public void testDijkstraUndirected() { + final UndirectedGraph undirected = new UndirectedGraph(); + final Graph.Vertex start = undirected.v1; + final Graph.Vertex end = undirected.v5; + { // UNDIRECTED GRAPH + final Map, Graph.CostPathPair> map1 = Dijkstra.getShortestPaths(undirected.graph, start); + + // Compare results + for (Graph.Vertex v : map1.keySet()) { + final Graph.CostPathPair path1 = map1.get(v); + final Graph.CostPathPair path2 = getIdealUndirectedPath(undirected).get(v); + assertTrue("Dijkstra's shortest path error. path1="+path1+" path2="+path2, path1.equals(path2)); + } + + final Graph.CostPathPair pair1 = Dijkstra.getShortestPath(undirected.graph, start, end); + assertTrue("No path from " + start.getValue() + " to " + end.getValue(), (pair1 != null)); + + assertTrue("Dijkstra's shortest path error. pair="+pair1+" pair="+getIdealUndirectedPathPair(undirected), pair1.equals(getIdealUndirectedPathPair(undirected))); + } + } + + @Test + public void testBellmanFordUndirected() { + final UndirectedGraph undirected = new UndirectedGraph(); + final Graph.Vertex start = undirected.v1; + final Graph.Vertex end = undirected.v5; + { + final Map, Graph.CostPathPair> map2 = BellmanFord.getShortestPaths(undirected.graph, start); + + // Compare results + for (Graph.Vertex v : map2.keySet()) { + final Graph.CostPathPair path1 = map2.get(v); + final Graph.CostPathPair path2 = getIdealUndirectedPath(undirected).get(v); + assertTrue("Bellman-Ford's shortest path error. path1="+path1+" path2="+path2, path1.equals(path2)); + } + + final Graph.CostPathPair pair2 = BellmanFord.getShortestPath(undirected.graph, start, end); + assertTrue("Bellman-Ford's shortest path error. pair="+pair2+" result="+getIdealUndirectedPathPair(undirected), pair2.equals(getIdealUndirectedPathPair(undirected))); + } + } + + @Test + public void testPrimUndirected() { + final UndirectedGraph undirected = new UndirectedGraph(); + + Graph.Vertex start = undirected.v1; + { + final Graph.CostPathPair resultMST = Prim.getMinimumSpanningTree(undirected.graph, start); + { + // Ideal MST + final int cost = 35; + final List> list = new ArrayList>(); + list.add(undirected.e1_7); + list.add(undirected.e1_8); + list.add(undirected.e1_2); + list.add(undirected.e1_3); + list.add(undirected.e3_6); + list.add(new Graph.Edge(9, undirected.v6, undirected.v5)); + list.add(new Graph.Edge(6, undirected.v5, undirected.v4)); + final Graph.CostPathPair idealMST = new Graph.CostPathPair(cost, list); + + assertTrue("Prim's minimum spanning tree error. resultMST="+resultMST+" idealMST="+idealMST, resultMST.equals(idealMST)); + } + + // Prim on a graph with cycles + final List> cyclicVerticies = new ArrayList>(); + final Graph.Vertex cv1 = new Graph.Vertex(1); + cyclicVerticies.add(cv1); + final Graph.Vertex cv2 = new Graph.Vertex(2); + cyclicVerticies.add(cv2); + final Graph.Vertex cv3 = new Graph.Vertex(3); + cyclicVerticies.add(cv3); + final Graph.Vertex cv4 = new Graph.Vertex(4); + cyclicVerticies.add(cv4); + final Graph.Vertex cv5 = new Graph.Vertex(5); + cyclicVerticies.add(cv5); + + final List> cyclicEdges = new ArrayList>(); + final Graph.Edge ce1_2 = new Graph.Edge(3, cv1, cv2); + cyclicEdges.add(ce1_2); + final Graph.Edge ce2_3 = new Graph.Edge(2, cv2, cv3); + cyclicEdges.add(ce2_3); + final Graph.Edge ce3_4 = new Graph.Edge(4, cv3, cv4); + cyclicEdges.add(ce3_4); + final Graph.Edge ce4_1 = new Graph.Edge(1, cv4, cv1); + cyclicEdges.add(ce4_1); + final Graph.Edge ce4_5 = new Graph.Edge(1, cv4, cv5); + cyclicEdges.add(ce4_5); + + final Graph cyclicUndirected = new Graph(TYPE.UNDIRECTED, cyclicVerticies, cyclicEdges); + + start = cv1; + + final Graph.CostPathPair pair4 = Prim.getMinimumSpanningTree(cyclicUndirected, start); + { + // Ideal MST + final int cost = 7; + final List> list = new ArrayList>(); + list.add(new Graph.Edge(1, cv1, cv4)); + list.add(ce4_5); + list.add(ce1_2); + list.add(ce2_3); + final Graph.CostPathPair result4 = new Graph.CostPathPair(cost, list); + + assertTrue("Prim's minimum spanning tree error. pair4="+pair4+" result4="+result4, pair4.equals(result4)); + } + } + } + + @Test + public void testKruskalUndirected() { + final UndirectedGraph undirected = new UndirectedGraph(); + { + final Graph.CostPathPair resultMST = Kruskal.getMinimumSpanningTree(undirected.graph); + { + // Ideal MST + final int cost = 35; + final List> list = new ArrayList>(); + list.add(undirected.e1_7); + list.add(undirected.e1_8); + list.add(undirected.e1_2); + list.add(undirected.e1_3); + list.add(undirected.e3_6); + list.add(new Graph.Edge(9, undirected.v6, undirected.v5)); + list.add(new Graph.Edge(6, undirected.v5, undirected.v4)); + final Graph.CostPathPair idealMST = new Graph.CostPathPair(cost, list); + + assertTrue("Kruskal's minimum spanning tree error. resultMST=" + resultMST + " idealMST=" + idealMST, resultMST.getCost() == idealMST.getCost()); + } + + // Kruskal on a graph with cycles + final List> cyclicVertices = new ArrayList>(); + final Graph.Vertex cv1 = new Graph.Vertex(1); + cyclicVertices.add(cv1); + final Graph.Vertex cv2 = new Graph.Vertex(2); + cyclicVertices.add(cv2); + final Graph.Vertex cv3 = new Graph.Vertex(3); + cyclicVertices.add(cv3); + final Graph.Vertex cv4 = new Graph.Vertex(4); + cyclicVertices.add(cv4); + final Graph.Vertex cv5 = new Graph.Vertex(5); + cyclicVertices.add(cv5); + + final List> cyclicEdges = new ArrayList>(); + final Graph.Edge ce1_2 = new Graph.Edge(3, cv1, cv2); + cyclicEdges.add(ce1_2); + final Graph.Edge ce2_3 = new Graph.Edge(2, cv2, cv3); + cyclicEdges.add(ce2_3); + final Graph.Edge ce3_4 = new Graph.Edge(4, cv3, cv4); + cyclicEdges.add(ce3_4); + final Graph.Edge ce4_1 = new Graph.Edge(1, cv4, cv1); + cyclicEdges.add(ce4_1); + final Graph.Edge ce4_5 = new Graph.Edge(1, cv4, cv5); + cyclicEdges.add(ce4_5); + + final Graph cyclicUndirected = new Graph(TYPE.UNDIRECTED, cyclicVertices, cyclicEdges); + + + final Graph.CostPathPair pair4 = Kruskal.getMinimumSpanningTree(cyclicUndirected); + { + // Ideal MST + final int cost = 7; + final List> list = new ArrayList>(); + list.add(new Graph.Edge(1, cv1, cv4)); + list.add(ce4_5); + list.add(ce1_2); + list.add(ce2_3); + final Graph.CostPathPair result4 = new Graph.CostPathPair(cost, list); + + assertTrue("Kruskal's minimum spanning tree error. pair4=" + pair4 + " result4=" + result4, pair4.getCost() == result4.getCost()); + } + } + } + + @Test + public void testDijkstraDirected() { + final DirectedGraph directed = new DirectedGraph(); + final Graph.Vertex start = directed.v1; + final Graph.Vertex end = directed.v5; + final Map, Graph.CostPathPair> map1 = Dijkstra.getShortestPaths(directed.graph, start); + + // Compare results + for (Graph.Vertex v : map1.keySet()) { + final Graph.CostPathPair path1 = map1.get(v); + final Graph.CostPathPair path2 = getIdealDirectedPath(directed).get(v); + assertTrue("Dijkstra's shortest path error. path1="+path1+" path2="+path2, path1.equals(path2)); + } + + final Graph.CostPathPair pair1 = Dijkstra.getShortestPath(directed.graph, start, end); + assertTrue("No path from "+start.getValue()+" to "+end.getValue(), (pair1!=null)); + + // Compare pair + assertTrue("Dijkstra's shortest path error. pair1="+pair1+" idealPathPair="+getIdealPathPair(directed), pair1.equals(getIdealPathPair(directed))); + } + + @Test + public void testBellmanFordDirected() { + final DirectedGraph directed = new DirectedGraph(); + final Graph.Vertex start = directed.v1; + final Graph.Vertex end = directed.v5; + + final Map, Graph.CostPathPair> map2 = BellmanFord.getShortestPaths(directed.graph, start); + + // Compare results + for (Graph.Vertex v : map2.keySet()) { + final Graph.CostPathPair path1 = map2.get(v); + final Graph.CostPathPair path2 = getIdealDirectedPath(directed).get(v); + assertTrue("Bellman-Ford's shortest path error. path1="+path1+" path2="+path2, path1.equals(path2)); + } + + final Graph.CostPathPair pair2 = BellmanFord.getShortestPath(directed.graph, start, end); + assertTrue("No path from "+start.getValue()+" to "+end.getValue(), pair2!=null); + + // Compare pair + assertTrue("Bellman-Ford's shortest path error. pair2="+pair2+" idealPathPair="+getIdealPathPair(directed), pair2.equals(getIdealPathPair(directed))); + } + + @Test + public void testDijkstraDirectedWihtNegativeWeights() { + final DirectedWithNegativeWeights directedWithNegWeights = new DirectedWithNegativeWeights(); + { // DIRECTED GRAPH (WITH NEGATIVE WEIGHTS) + final Graph.Vertex start = directedWithNegWeights.v1; + final Graph.Vertex end = directedWithNegWeights.v3; + final Map, Graph.CostPathPair> map1 = BellmanFord.getShortestPaths(directedWithNegWeights.graph, start); + + // Compare results + for (Graph.Vertex v : map1.keySet()) { + final Graph.CostPathPair path1 = map1.get(v); + final Graph.CostPathPair path2 = getIdealDirectedNegWeight(directedWithNegWeights).get(v); + assertTrue("Bellman-Ford's shortest path error. path1="+path1+" path2="+path2, path1.equals(path2)); + } + + final Graph.CostPathPair pair1 = BellmanFord.getShortestPath(directedWithNegWeights.graph, start, end); + assertTrue("No path from " + start.getValue() + " to " + end.getValue(), pair1 != null); + + // Compare pair + assertTrue("Bellman-Ford's shortest path error. pair1="+pair1+" result2="+getIdealDirectedWithNegWeightsPathPair(directedWithNegWeights), pair1.equals(getIdealDirectedWithNegWeightsPathPair(directedWithNegWeights))); + } + } + + @Test + public void testJohnonsonsAllPairsShortestPathOnDirecteWithNegWeights() { + final DirectedWithNegativeWeights directedWithNegWeights = new DirectedWithNegativeWeights(); + { + final Map, Map, List>>> path = Johnson.getAllPairsShortestPaths(directedWithNegWeights.graph); + assertTrue("Directed graph contains a negative weight cycle.", (path != null)); + + final Map, Map, List>>> result = new HashMap, Map, List>>>(); + { + { // vertex 3 + final Map, List>> m = new HashMap, List>>(); + final List> s3 = new ArrayList>(); + m.put(directedWithNegWeights.v3, s3); + final List> s4 = new ArrayList>(); + s4.add(directedWithNegWeights.e3_4); + m.put(directedWithNegWeights.v4, s4); + final List> s2 = new ArrayList>(); + s2.add(directedWithNegWeights.e3_4); + s2.add(directedWithNegWeights.e4_2); + m.put(directedWithNegWeights.v2, s2); + final List> s1 = new ArrayList>(); + s1.add(directedWithNegWeights.e3_1); + m.put(directedWithNegWeights.v1, s1); + result.put(directedWithNegWeights.v3, m); + } + { // vertex 4 + final Map, List>> m = new HashMap, List>>(); + final List> s3 = new ArrayList>(); + s3.add(directedWithNegWeights.e4_2); + s3.add(directedWithNegWeights.e2_3); + m.put(directedWithNegWeights.v3, s3); + final List> s4 = new ArrayList>(); + m.put(directedWithNegWeights.v4, s4); + final List> s2 = new ArrayList>(); + s2.add(directedWithNegWeights.e4_2); + m.put(directedWithNegWeights.v2, s2); + final List> s1 = new ArrayList>(); + s1.add(directedWithNegWeights.e4_2); + s1.add(directedWithNegWeights.e2_1); + m.put(directedWithNegWeights.v1, s1); + result.put(directedWithNegWeights.v4, m); + } + { // vertex 2 + final Map, List>> m = new HashMap, List>>(); + final List> s3 = new ArrayList>(); + s3.add(directedWithNegWeights.e2_3); + m.put(directedWithNegWeights.v3, s3); + final List> s4 = new ArrayList>(); + s4.add(directedWithNegWeights.e2_1); + s4.add(directedWithNegWeights.e1_4); + m.put(directedWithNegWeights.v4, s4); + final List> s2 = new ArrayList>(); + m.put(directedWithNegWeights.v2, s2); + final List> s1 = new ArrayList>(); + s1.add(directedWithNegWeights.e2_1); + m.put(directedWithNegWeights.v1, s1); + result.put(directedWithNegWeights.v2, m); + } + { // vertex 1 + final Map, List>> m = new HashMap, List>>(); + final List> s3 = new ArrayList>(); + s3.add(directedWithNegWeights.e1_4); + s3.add(directedWithNegWeights.e4_2); + s3.add(directedWithNegWeights.e2_3); + m.put(directedWithNegWeights.v3, s3); + final List> s4 = new ArrayList>(); + s4.add(directedWithNegWeights.e1_4); + m.put(directedWithNegWeights.v4, s4); + final List> s2 = new ArrayList>(); + s2.add(directedWithNegWeights.e1_4); + s2.add(directedWithNegWeights.e4_2); + m.put(directedWithNegWeights.v2, s2); + final List> s1 = new ArrayList>(); + m.put(directedWithNegWeights.v1, s1); + result.put(directedWithNegWeights.v1, m); + } + } + + // Compare results + for (Vertex vertex1 : path.keySet()) { + final Map, List>> map1 = path.get(vertex1); + final Map, List>> map2 = result.get(vertex1); + for (Vertex vertex2 : map1.keySet()) { + final List> list1 = map1.get(vertex2); + final List> list2 = map2.get(vertex2); + final Iterator> iter1 = list1.iterator(); + final Iterator> iter2 = list2.iterator(); + assertTrue("Johnson's all-pairs shortest path error. size3="+list1.size()+" size4="+list2.size(), list1.size()==list2.size()); + + while (iter1.hasNext() && iter2.hasNext()) { + Edge e1 = (Edge) iter1.next(); + Edge e2 = (Edge) iter2.next(); + assertTrue("Johnson's all-pairs shortest path error. e1.from="+e1.getFromVertex()+" e2.from="+e2.getFromVertex(), + e1.getFromVertex().equals(e2.getFromVertex())); + assertTrue("Johnson's all-pairs shortest path error. e1.to="+e1.getToVertex()+" e2.to="+e2.getToVertex(), + e1.getToVertex().equals(e2.getToVertex())); + } + } + } + } + } + + @Test + public void testFloydWarshallonDirectedWithNegWeights() { + final DirectedWithNegativeWeights directedWithNegWeights = new DirectedWithNegativeWeights(); + { + final Map, Map, Integer>> pathWeights = FloydWarshall.getAllPairsShortestPaths(directedWithNegWeights.graph); + final Map, Map, Integer>> result = new HashMap, Map, Integer>>(); + { + // Ideal weights + { // Vertex 3 + final Map, Integer> m = new HashMap, Integer>(); + { + // Vertex 3 + m.put(directedWithNegWeights.v3, 0); + // Vertex 4 + m.put(directedWithNegWeights.v4, 5); + // Vertex 2 + m.put(directedWithNegWeights.v2, -2); + // Vertex 1 + m.put(directedWithNegWeights.v1, 4); + } + result.put(directedWithNegWeights.v3, m); + } + { // Vertex 4 + final Map, Integer> m = new HashMap, Integer>(); + { + // Vertex 3 + m.put(directedWithNegWeights.v3, -4); + // Vertex 4 + m.put(directedWithNegWeights.v4, 0); + // Vertex 2 + m.put(directedWithNegWeights.v2, -7); + // Vertex 1 + m.put(directedWithNegWeights.v1, -1); + } + result.put(directedWithNegWeights.v4, m); + } + { // Vertex 2 + final Map, Integer> m = new HashMap, Integer>(); + { + // Vertex 3 + m.put(directedWithNegWeights.v3, 3); + // Vertex 4 + m.put(directedWithNegWeights.v4, 8); + // Vertex 2 + m.put(directedWithNegWeights.v2, 0); + // Vertex 1 + m.put(directedWithNegWeights.v1, 6); + } + result.put(directedWithNegWeights.v2, m); + } + { // Vertex 1 + final Map, Integer> m = new HashMap, Integer>(); + { + // Vertex 3 + m.put(directedWithNegWeights.v3, -2); + // Vertex 4 + m.put(directedWithNegWeights.v4, 2); + // Vertex 2 + m.put(directedWithNegWeights.v2, -5); + // Vertex 1 + m.put(directedWithNegWeights.v1, 0); + } + result.put(directedWithNegWeights.v1, m); + } + } + + // Compare results + for (Vertex vertex1 : pathWeights.keySet()) { + final Map, Integer> m1 = pathWeights.get(vertex1); + final Map, Integer> m2 = result.get(vertex1); + for (Vertex v : m1.keySet()) { + final int i1 = m1.get(v); + final int i2 = m2.get(v); + assertTrue("Floyd-Warshall's all-pairs shortest path weights error. i1="+i1+" i2="+i2, i1 == i2); + } + + } + } + } + + @Test + public void cycleCheckOnUndirected() { + final List> cycledVerticies = new ArrayList>(); + { // UNDIRECTED GRAPH + final Graph.Vertex cv1 = new Graph.Vertex(1); + cycledVerticies.add(cv1); + final Graph.Vertex cv2 = new Graph.Vertex(2); + cycledVerticies.add(cv2); + final Graph.Vertex cv3 = new Graph.Vertex(3); + cycledVerticies.add(cv3); + final Graph.Vertex cv4 = new Graph.Vertex(4); + cycledVerticies.add(cv4); + final Graph.Vertex cv5 = new Graph.Vertex(5); + cycledVerticies.add(cv5); + final Graph.Vertex cv6 = new Graph.Vertex(6); + cycledVerticies.add(cv6); + + final List> cycledEdges = new ArrayList>(); + final Graph.Edge ce1_2 = new Graph.Edge(7, cv1, cv2); + cycledEdges.add(ce1_2); + final Graph.Edge ce2_4 = new Graph.Edge(15, cv2, cv4); + cycledEdges.add(ce2_4); + final Graph.Edge ce3_4 = new Graph.Edge(11, cv3, cv4); + cycledEdges.add(ce3_4); + final Graph.Edge ce3_6 = new Graph.Edge(2, cv3, cv6); + cycledEdges.add(ce3_6); + final Graph.Edge ce5_6 = new Graph.Edge(9, cv5, cv6); + cycledEdges.add(ce5_6); + final Graph.Edge ce4_5 = new Graph.Edge(6, cv4, cv5); + cycledEdges.add(ce4_5); + + final Graph undirectedWithCycle = new Graph(cycledVerticies, cycledEdges); + + boolean result = CycleDetection.detect(undirectedWithCycle); + assertTrue("Cycle detection error.", result); + + final List> verticies = new ArrayList>(); + final Graph.Vertex v1 = new Graph.Vertex(1); + verticies.add(v1); + final Graph.Vertex v2 = new Graph.Vertex(2); + verticies.add(v2); + final Graph.Vertex v3 = new Graph.Vertex(3); + verticies.add(v3); + final Graph.Vertex v4 = new Graph.Vertex(4); + verticies.add(v4); + final Graph.Vertex v5 = new Graph.Vertex(5); + verticies.add(v5); + final Graph.Vertex v6 = new Graph.Vertex(6); + verticies.add(v6); + + final List> edges = new ArrayList>(); + final Graph.Edge e1_2 = new Graph.Edge(7, v1, v2); + edges.add(e1_2); + final Graph.Edge e2_4 = new Graph.Edge(15, v2, v4); + edges.add(e2_4); + final Graph.Edge e3_4 = new Graph.Edge(11, v3, v4); + edges.add(e3_4); + final Graph.Edge e3_6 = new Graph.Edge(2, v3, v6); + edges.add(e3_6); + final Graph.Edge e4_5 = new Graph.Edge(6, v4, v5); + edges.add(e4_5); + + final Graph undirectedWithoutCycle = new Graph(verticies, edges); + + result = CycleDetection.detect(undirectedWithoutCycle); + assertFalse("Cycle detection error.", result); + } + } + + @Test + public void topologicalSortOnDirectedGraph() { + { // DIRECTED GRAPH + final List> verticies = new ArrayList>(); + final Graph.Vertex cv1 = new Graph.Vertex(1); + verticies.add(cv1); + final Graph.Vertex cv2 = new Graph.Vertex(2); + verticies.add(cv2); + final Graph.Vertex cv3 = new Graph.Vertex(3); + verticies.add(cv3); + final Graph.Vertex cv4 = new Graph.Vertex(4); + verticies.add(cv4); + final Graph.Vertex cv5 = new Graph.Vertex(5); + verticies.add(cv5); + final Graph.Vertex cv6 = new Graph.Vertex(6); + verticies.add(cv6); + + final List> edges = new ArrayList>(); + final Graph.Edge ce1_2 = new Graph.Edge(1, cv1, cv2); + edges.add(ce1_2); + final Graph.Edge ce2_4 = new Graph.Edge(2, cv2, cv4); + edges.add(ce2_4); + final Graph.Edge ce4_3 = new Graph.Edge(3, cv4, cv3); + edges.add(ce4_3); + final Graph.Edge ce3_6 = new Graph.Edge(4, cv3, cv6); + edges.add(ce3_6); + final Graph.Edge ce5_6 = new Graph.Edge(5, cv5, cv6); + edges.add(ce5_6); + final Graph.Edge ce4_5 = new Graph.Edge(6, cv4, cv5); + edges.add(ce4_5); + + final Graph digraph = new Graph(Graph.TYPE.DIRECTED, verticies, edges); + + final List> results1 = TopologicalSort.sort(digraph); + assertTrue("Topological sort error. results="+results1, results1.size()!=0); + + final List> results2 = new ArrayList>(results1.size()); + { // Ideal sort + results2.add(cv6); + results2.add(cv3); + results2.add(cv5); + results2.add(cv4); + results2.add(cv2); + results2.add(cv1); + } + + // Compare results + { + final Iterator> iter1 = results1.iterator(); + final Iterator> iter2 = results2.iterator(); + assertTrue("Topological sort error. size1="+results1.size()+" size2="+results2.size(), results1.size()==results2.size()); + while (iter1.hasNext() && iter2.hasNext()) { + final Graph.Vertex v1 = (Graph.Vertex) iter1.next(); + final Graph.Vertex v2 = (Graph.Vertex) iter2.next(); + assertTrue("Topological sort error. v1="+v1+" v2="+v2, v1.equals(v2)); + } + } + } + } + + @Test + public void connectedComponents() { + { + final Graph g = makeDirectedGraph(3, 2, new int[]{5, 4, 7}); + Assert.assertTrue(ConnectedComponents.getConnectedComponents(g).size()==2); + } + + { + final Graph g = makeDirectedGraph(5, 1, new int[]{5, 3, 4, 5, 6}); + Assert.assertTrue(ConnectedComponents.getConnectedComponents(g).size()==1); + } + + { + final Graph g = makeDirectedGraph(8, 51, new int[]{49, 57, 3, 95, 98, 100, 44, 40}); + Assert.assertTrue(ConnectedComponents.getConnectedComponents(g).size()==3); + } + + { + final Graph g = makeDirectedGraph(28, 79, new int[]{123, 60, 227, 766, 400, 405, 24, 968, 359, 533, 689, 409, + 188, 677, 231, 295, 240, 52, 373, 243, 493, 645, 307, 781, + 523, 494, 950, 899}); + Assert.assertTrue(ConnectedComponents.getConnectedComponents(g).size()==3); + } + + { + final Graph g = makeDirectedGraph(15, 564, new int[]{617, 400, 658, 30, 891, 517, 304, 156, 254, 610, 72, 371, + 411, 689, 381}); + Assert.assertTrue(ConnectedComponents.getConnectedComponents(g).size()==10); + } + + { + final Graph g = makeDirectedGraph(13, 422, new int[]{87, 950, 773, 928, 696, 131, 809, 781, 348, 144, 717, 555, 311}); + Assert.assertTrue(ConnectedComponents.getConnectedComponents(g).size()==6); + } + + { + final Graph g = makeDirectedGraph(17, 599, new int[]{903, 868, 67, 690, 841, 815, 469, 554, 647, 235, 787, 221, 669, + 87, 60, 28, 324}); + Assert.assertTrue(ConnectedComponents.getConnectedComponents(g).size()==10); + } + } + + @Test + public void testAStarUndirected() { + final UndirectedGraph undirected = new UndirectedGraph(); + final Graph.Vertex start = undirected.v1; + final Graph.Vertex end = undirected.v8; + { // UNDIRECTED GRAPH + final AStar aStar = new AStar(); + final List> path = aStar.aStar(undirected.graph, start, end); + final List> ideal = getIdealUndirectedPath(undirected).get(end).getPath(); + assertTrue("A* path error. path="+path+" idealPathPair="+ideal, path.equals(ideal)); + } + } + + @Test + public void testAStarDirected() { + final DirectedGraph directed = new DirectedGraph(); + final Graph.Vertex start = directed.v1; + final Graph.Vertex end = directed.v8; + { // DIRECTED GRAPH + final AStar aStar = new AStar(); + final List> path = aStar.aStar(directed.graph, start, end); + final List> ideal = getIdealDirectedPath(directed).get(end).getPath(); + assertTrue("A* path error. path="+path+" idealPathPair="+ideal, path.equals(ideal)); + } + } + + /* + * Makes a zero weighted directed graph, so that there is an edge between two vertices if the difference between the + * vertices values is >= K + */ + @SuppressWarnings("unused") + private static final Graph makeDirectedGraph(int N, int K, int[] values) { + final List> vertices = new ArrayList>(values.length); + for (int i=0; i(values[i], 0)); + + final List> edges = new ArrayList>(values.length); + for (int i=0; i vi = vertices.get(i); + for (int j=i+1; j vj = vertices.get(j); + final int diff = Math.abs(vi.getValue() - vj.getValue()); + if (diff >= K) { + final Edge eij = new Edge(diff, vi, vj); + edges.add(eij); + } + } + } + + final Graph g = new Graph(TYPE.DIRECTED, vertices, edges); + return g; + } +} diff --git a/test/com/jwetherell/algorithms/graph/test/PushRelabelTest.java b/test/com/jwetherell/algorithms/graph/test/PushRelabelTest.java new file mode 100644 index 00000000..cb586ed2 --- /dev/null +++ b/test/com/jwetherell/algorithms/graph/test/PushRelabelTest.java @@ -0,0 +1,91 @@ +package com.jwetherell.algorithms.graph.test; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.graph.PushRelabel; +import org.junit.Test; + +import java.util.Map; +import java.util.TreeMap; + +import static org.junit.Assert.assertTrue; + +public class PushRelabelTest { + + private static class ExampleInput1 { + + final Graph.Vertex v1 = new Graph.Vertex(1); + final Graph.Vertex v2 = new Graph.Vertex(2); + final Graph.Vertex v3 = new Graph.Vertex(3); + final Graph.Vertex v4 = new Graph.Vertex(4); + + final Graph.Edge e1_2 = new Graph.Edge(1, v1, v2); + final Graph.Edge e1_3 = new Graph.Edge(1, v1, v3); + final Graph.Edge e2_3 = new Graph.Edge(1, v2, v3); + final Graph.Edge e3_2 = new Graph.Edge(1, v3, v2); + final Graph.Edge e2_4 = new Graph.Edge(1, v2, v4); + final Graph.Edge e3_4 = new Graph.Edge(1, v3, v4); + + final Map, Long> capacities = new TreeMap, Long>(); + { + capacities.put(e1_2, 3L); + capacities.put(e1_3, 5L); + capacities.put(e3_2, 3L); + capacities.put(e2_3, 2L); + capacities.put(e2_4, 7L); + capacities.put(e3_4, 1L); + } + + final Graph.Vertex source = v1; + final Graph.Vertex sink = v4; + } + + @Test + public void testPushRelabel1() { + final ExampleInput1 exampleInput = new ExampleInput1(); + assertTrue(PushRelabel.getMaximumFlow(exampleInput.capacities, exampleInput.source, exampleInput.sink) == 7); + } + + private static class ExampleInput2 { + + final Graph.Vertex v0 = new Graph.Vertex(0); + final Graph.Vertex v1 = new Graph.Vertex(1); + final Graph.Vertex v2 = new Graph.Vertex(2); + final Graph.Vertex v3 = new Graph.Vertex(3); + final Graph.Vertex v4 = new Graph.Vertex(4); + final Graph.Vertex v5 = new Graph.Vertex(5); + + final Graph.Edge e0_1 = new Graph.Edge(1, v0, v1); + final Graph.Edge e0_2 = new Graph.Edge(1, v0, v2); + final Graph.Edge e1_2 = new Graph.Edge(1, v1, v2); + final Graph.Edge e1_3 = new Graph.Edge(1, v1, v3); + final Graph.Edge e2_1 = new Graph.Edge(1, v2, v1); + final Graph.Edge e2_4 = new Graph.Edge(1, v2, v4); + final Graph.Edge e3_2 = new Graph.Edge(1, v3, v2); + final Graph.Edge e3_5 = new Graph.Edge(1, v3, v5); + final Graph.Edge e4_3 = new Graph.Edge(1, v4, v3); + final Graph.Edge e4_5 = new Graph.Edge(1, v4, v5); + + final Map, Long> capacities = new TreeMap, Long>(); + { + capacities.put(e0_1, 16L); + capacities.put(e0_2, 13L); + capacities.put(e1_2, 10L); + capacities.put(e1_3, 12L); + capacities.put(e2_1, 4L); + capacities.put(e2_4, 14L); + capacities.put(e3_2, 9L); + capacities.put(e3_5, 20L); + capacities.put(e4_3, 7L); + capacities.put(e4_5, 4L); + } + + final Graph.Vertex source = v0; + final Graph.Vertex sink = v5; + } + + @Test + public void testPushRelabel2() { + final ExampleInput2 exampleInput = new ExampleInput2(); + assertTrue(PushRelabel.getMaximumFlow(exampleInput.capacities, exampleInput.source, exampleInput.sink) == 23); + } +} diff --git a/test/com/jwetherell/algorithms/graph/test/TurboMatchingTest.java b/test/com/jwetherell/algorithms/graph/test/TurboMatchingTest.java new file mode 100644 index 00000000..5e9be687 --- /dev/null +++ b/test/com/jwetherell/algorithms/graph/test/TurboMatchingTest.java @@ -0,0 +1,134 @@ +package com.jwetherell.algorithms.graph.test; + +import com.jwetherell.algorithms.data_structures.Graph; +import com.jwetherell.algorithms.graph.TurboMatching; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + + +public class TurboMatchingTest { + + private final Graph.Vertex v_a1 = new Graph.Vertex(1); + private final Graph.Vertex v_a2 = new Graph.Vertex(2); + private final Graph.Vertex v_a3 = new Graph.Vertex(3); + private final Graph.Vertex v_b1 = new Graph.Vertex(4); + private final Graph.Vertex v_b2 = new Graph.Vertex(5); + private final Graph.Vertex v_b3 = new Graph.Vertex(6); + + private List> vertices = new ArrayList>(); + + { + vertices.add(v_a1); + vertices.add(v_a2); + vertices.add(v_a3); + vertices.add(v_b1); + vertices.add(v_b2); + vertices.add(v_b3); + } + @Test + public void testFullBipartiteGraph(){ + List> edges = new ArrayList>(); + { + edges.add(new Graph.Edge(1, v_a1, v_b1)); + edges.add(new Graph.Edge(1, v_a1, v_b2)); + edges.add(new Graph.Edge(1, v_a1, v_b3)); + edges.add(new Graph.Edge(1, v_a2, v_b1)); + edges.add(new Graph.Edge(1, v_a2, v_b2)); + edges.add(new Graph.Edge(1, v_a2, v_b3)); + edges.add(new Graph.Edge(1, v_a3, v_b1)); + edges.add(new Graph.Edge(1, v_a3, v_b2)); + edges.add(new Graph.Edge(1, v_a3, v_b3)); + } + + final Graph graph = new Graph(vertices, edges); + + TurboMatching.MatchingResult matchingResult = TurboMatching.getMaximumMatching(graph); + assertTrue(matchingResult.getSize() == 3); + for(Graph.Vertex vertex : vertices){ + assertTrue(matchingResult.getMate().get(matchingResult.getMate().get(vertex)).equals(vertex)); + } + } + + @Test + public void testSingleEdgeForVertex(){ + List> edges = new ArrayList>(); + { + edges.add(new Graph.Edge(1, v_a1, v_b1)); + edges.add(new Graph.Edge(1, v_a2, v_b2)); + edges.add(new Graph.Edge(1, v_a3, v_b3)); + } + + final Graph graph = new Graph(vertices, edges); + + TurboMatching.MatchingResult matchingResult = TurboMatching.getMaximumMatching(graph); + + assertTrue(matchingResult.getSize() == 3); + assertTrue(matchingResult.getMate().get(v_a1).equals(v_b1)); + assertTrue(matchingResult.getMate().get(v_a2).equals(v_b2)); + assertTrue(matchingResult.getMate().get(v_a3).equals(v_b3)); + assertTrue(matchingResult.getMate().get(v_b1).equals(v_a1)); + assertTrue(matchingResult.getMate().get(v_b2).equals(v_a2)); + assertTrue(matchingResult.getMate().get(v_b3).equals(v_a3)); + } + + @Test + public void testEmptyGraph(){ + List> edges = new ArrayList>(); + { + } + + final Graph graph = new Graph(vertices, edges); + + TurboMatching.MatchingResult matchingResult = TurboMatching.getMaximumMatching(graph); + + assertTrue(matchingResult.getSize() == 0); + assertTrue(matchingResult.getMate().isEmpty()); + } + + @Test + public void testTwoMatched(){ + List> edges = new ArrayList>(); + { + edges.add(new Graph.Edge(1, v_a1, v_b1)); + edges.add(new Graph.Edge(1, v_a1, v_b3)); + edges.add(new Graph.Edge(1, v_a2, v_b2)); + edges.add(new Graph.Edge(1, v_a3, v_b2)); + } + + final Graph graph = new Graph(vertices, edges); + TurboMatching.MatchingResult matchingResult = TurboMatching.getMaximumMatching(graph); + + assertTrue(matchingResult.getSize() == 2); + assertTrue(matchingResult.getMate().containsKey(v_a1)); + assertTrue(matchingResult.getMate().containsKey(v_b2)); + assertTrue(matchingResult.getMate().containsValue(v_a1)); + assertTrue(matchingResult.getMate().containsValue(v_b2)); + } + + @Test + public void testOneMatched(){ + List> edges = new ArrayList>(); + { + edges.add(new Graph.Edge(1, v_a1, v_b1)); + edges.add(new Graph.Edge(1, v_a1, v_b2)); + edges.add(new Graph.Edge(1, v_a1, v_b3)); + } + + final Graph graph = new Graph(vertices, edges); + TurboMatching.MatchingResult matchingResult = TurboMatching.getMaximumMatching(graph); + + assertTrue(matchingResult.getSize() == 1); + assertTrue(matchingResult.getMate().containsKey(v_a1)); + assertTrue(matchingResult.getMate().containsValue(v_a1)); + assertFalse(matchingResult.getMate().containsKey(v_a2)); + assertFalse(matchingResult.getMate().containsValue(v_a2)); + assertFalse(matchingResult.getMate().containsKey(v_a3)); + assertFalse(matchingResult.getMate().containsValue(v_a3)); + } + + +} \ No newline at end of file diff --git a/test/com/jwetherell/algorithms/mathematics/test/CoprimesTest.java b/test/com/jwetherell/algorithms/mathematics/test/CoprimesTest.java new file mode 100644 index 00000000..386aeda6 --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/test/CoprimesTest.java @@ -0,0 +1,21 @@ +package com.jwetherell.algorithms.mathematics.test; + +import com.jwetherell.algorithms.mathematics.Coprimes; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class CoprimesTest { + + @Test + public void totientTest(){ + final List args = Arrays.asList(1L, 17L, 96L, 498L, 4182119424L); + final List expected = Arrays.asList(1L, 16L, 32L, 164L, 1194891264L); + + for(int i = 0; i < args.size(); i++) + assertEquals(expected.get(i), Coprimes.getNumberOfCoprimes(args.get(i))); + } +} diff --git a/test/com/jwetherell/algorithms/mathematics/test/DiscreteLogarithmTest.java b/test/com/jwetherell/algorithms/mathematics/test/DiscreteLogarithmTest.java new file mode 100644 index 00000000..a30168ff --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/test/DiscreteLogarithmTest.java @@ -0,0 +1,43 @@ +package com.jwetherell.algorithms.mathematics.test; + +import org.junit.Test; + +import com.jwetherell.algorithms.mathematics.DiscreteLogarithm; + +import static org.junit.Assert.assertTrue; + +public class DiscreteLogarithmTest { + + @Test + public void shouldCountDiscreteLogarithm() { + final long a = 3; + final long b = 4; + final long p = 7; + final long expectedX = 4; + + final long x = DiscreteLogarithm.countDiscreteLogarithm(a, b, p); + assertTrue(x == expectedX); + } + + @Test + public void shouldCountDiscreteLogarithm2() { + final long a = 2; + final long b = 64; + final long p = 101; + final long expectedX = 6; + + final long x = DiscreteLogarithm.countDiscreteLogarithm(a, b, p); + assertTrue(x == expectedX); + } + + @Test + public void shouldNotCountDiscreteLogarithm() { + final long a = 4; + final long b = 5; + final long p = 7; + final long expectedX = -1; + + final long x = DiscreteLogarithm.countDiscreteLogarithm(a, b, p); + assertTrue(x == expectedX); + } +} \ No newline at end of file diff --git a/test/com/jwetherell/algorithms/mathematics/test/ExponentiationTest.java b/test/com/jwetherell/algorithms/mathematics/test/ExponentiationTest.java new file mode 100644 index 00000000..11ef4d52 --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/test/ExponentiationTest.java @@ -0,0 +1,45 @@ +package com.jwetherell.algorithms.mathematics.test; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import com.jwetherell.algorithms.mathematics.Exponentiation; + + +import static org.junit.Assert.assertEquals; + +public class ExponentiationTest { + + @Test + public void recusriveExponentiationTest() { + final List baseList = Arrays.asList(1, 2, 4, 6, 8, 17, 24); + final List exponentList = Arrays.asList(1000, 27, 14, 11, 10, 7, 5); + final List expectedResultList = Arrays.asList(1, 134217728, 268435456, 362797056, 1073741824, 410338673, 7962624); + + for (int i = 0; i < expectedResultList.size(); i++) + assertEquals(expectedResultList.get(i), Exponentiation.recursiveExponentiation(baseList.get(i), exponentList.get(i))); + } + + @Test + public void fastRecusriveExponentiationTest() { + final List baseList = Arrays.asList(1, 2, 4, 6, 8, 17, 24); + final List exponentList = Arrays.asList(1000, 27, 14, 11, 10, 7, 5); + final List expectedResultList = Arrays.asList(1, 134217728, 268435456, 362797056, 1073741824, 410338673, 7962624); + + for (int i = 0; i < expectedResultList.size(); i++) + assertEquals(expectedResultList.get(i), Exponentiation.fastRecursiveExponentiation(baseList.get(i), exponentList.get(i))); + } + + @Test + public void fastRecusriveExponentiationModuloTest() { + final List baseList = Arrays.asList(1, 2, 4, 6, 8, 17, 24, 2); + final List exponentList = Arrays.asList(1000, 27, 14, 11, 10, 7, 5, 1089); + final List divisorList = Arrays.asList(2, 6, 3, 2, 9, 11, 5, 2179); + final List expectedResultList = Arrays.asList(1, 2, 1, 0, 1, 8, 4, 2178); + + for (int i = 0; i < expectedResultList.size(); i++) + assertEquals(expectedResultList.get(i), Exponentiation.fastRecursiveExponentiationModulo(baseList.get(i), exponentList.get(i), divisorList.get(i))); + } +} diff --git a/test/com/jwetherell/algorithms/mathematics/test/GCDTest.java b/test/com/jwetherell/algorithms/mathematics/test/GCDTest.java new file mode 100644 index 00000000..318e90f1 --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/test/GCDTest.java @@ -0,0 +1,40 @@ +package com.jwetherell.algorithms.mathematics.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jwetherell.algorithms.mathematics.GreatestCommonDivisor; + + +public class GCDTest { + + @Test + public void testRecursiveGCD() { + assertEquals(3, GreatestCommonDivisor.gcdUsingRecursion(15, 138)); + assertEquals(79, GreatestCommonDivisor.gcdUsingRecursion(79, -79)); + assertEquals(750, GreatestCommonDivisor.gcdUsingRecursion(-750*1000000009L, -750*123)); + } + + @Test + public void testEuclideanGCD() { + long x = 1989; + long y = 867; + long gcd = GreatestCommonDivisor.gcdUsingEuclides(x, y); + long check = 51; + assertTrue("Euclids GCD error. expected="+check+" got="+gcd, (gcd==check)); + + x = 567; + y = 56; + gcd = GreatestCommonDivisor.gcdUsingEuclides(x, y); + check = 7; + assertTrue("Euclids GCD error. expected="+check+" got="+gcd, (gcd==check)); + + x = 10002345; + y = 67885; + gcd = GreatestCommonDivisor.gcdUsingEuclides(x, y); + check = 5; + assertTrue("Euclids GCD error. expected="+check+" got="+gcd, (gcd==check)); + } +} diff --git a/test/com/jwetherell/algorithms/mathematics/test/LUDecompositionTest.java b/test/com/jwetherell/algorithms/mathematics/test/LUDecompositionTest.java new file mode 100644 index 00000000..d52f4e65 --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/test/LUDecompositionTest.java @@ -0,0 +1,46 @@ +package com.jwetherell.algorithms.mathematics.test; + +import com.jwetherell.algorithms.data_structures.Matrix; +import com.jwetherell.algorithms.mathematics.LUDecomposition; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class LUDecompositionTest { + + private boolean epsiMatrixCompare(Matrix a, Matrix b, double epsi) { + if (a.getRows() != b.getRows() || a.getCols() != b.getCols()) { + throw new IllegalArgumentException("Matrices are not the same shape"); + } + for (int i = 0; i < a.getRows(); i++) { + for (int j = 0; j < a.getCols(); j++) { + if (Math.abs(a.get(i, j) - b.get(i, j)) > epsi) { + return false; + } + } + } + return true; + } + + @Test + public void decompositionTest1() throws Exception { + Double[][] m = new Double[][]{{4.0, 3.0}, {6.0, 3.0}}; + Double[][] resultL = new Double[][]{{1.0, 0.0}, {2.0 / 3.0, 1.0}}; + Double[][] resultU = new Double[][]{{6.0, 3.0}, {0.0, 1.0}}; + + LUDecomposition luDecomposition = new LUDecomposition(new Matrix(2, 2, m)); + assertTrue(epsiMatrixCompare(luDecomposition.getL(), new Matrix(2, 2, resultL), 10e-4)); + assertTrue(epsiMatrixCompare(luDecomposition.getU(), new Matrix(2, 2, resultU), 10e-4)); + } + + @Test + public void decompositionTest2() throws Exception { + Double[][] m = new Double[][]{{5.0, 3.0, 2.0}, {1.0, 2.0, 0.0}, {3.0, 0.0, 4.0}}; + Double[][] resultL = new Double[][]{{1.0, 0.0, 0.0}, {0.6, 1.0, 0.0}, {0.2, -0.7778, 1.0}}; + Double[][] resultU = new Double[][]{{5.0, 3.0, 2.0}, {0.0, -1.8, 2.8}, {0.0, 0.0, 1.778}}; + + LUDecomposition luDecomposition = new LUDecomposition(new Matrix(3, 3, m)); + assertTrue(epsiMatrixCompare(luDecomposition.getL(), new Matrix(3, 3, resultL), 10e-4)); + assertTrue(epsiMatrixCompare(luDecomposition.getU(), new Matrix(3, 3, resultU), 10e-4)); + } +} \ No newline at end of file diff --git a/test/com/jwetherell/algorithms/mathematics/test/MathematicsTest.java b/test/com/jwetherell/algorithms/mathematics/test/MathematicsTest.java new file mode 100644 index 00000000..c20381af --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/test/MathematicsTest.java @@ -0,0 +1,312 @@ +package com.jwetherell.algorithms.mathematics.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.*; + +import org.junit.Test; + +import com.jwetherell.algorithms.mathematics.Distance; +import com.jwetherell.algorithms.mathematics.Division; +import com.jwetherell.algorithms.mathematics.Knapsack; +import com.jwetherell.algorithms.mathematics.Multiplication; +import com.jwetherell.algorithms.mathematics.Primes; + +public class MathematicsTest { + + private static final int MIN = 1; + private static final int MAX = 1000; + + private static final Random RANDOM = new Random(); + + private static int nextRandomInt(int min, int max) { + // nextInt is normally exclusive of the top value, + // so add 1 to make it inclusive + return RANDOM.nextInt((max - min) + 1) + min; + } + + @Test + public void multiplication() { + int a = Math.abs(nextRandomInt(MIN, MAX)); + int b = Math.abs(nextRandomInt(MIN, MAX)); + // positive * positive + { + long result = Multiplication.multiplyUsingLoop(a, b); + long check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using a loop. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingRecursion(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using recursion. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingShift(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using shifts. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingLogs(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using logs. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Integer.parseInt(Multiplication.multiplyUsingFFT(Integer.toString(a), Integer.toString(b))); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using FFT. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Integer.parseInt(Multiplication.multiplyUsingLoopWithStringInput(Integer.toString(a), Integer.toString(b))); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using loop with string input. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingLoopWithIntegerInput(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using loop with int input. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + } + // negative * positive + a *= -1; + { + long result = Multiplication.multiplyUsingLoop(a, b); + long check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using a loop. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingRecursion(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using recursion. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingShift(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using shifts. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingLogs(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using logs. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Integer.parseInt(Multiplication.multiplyUsingFFT(Integer.toString(a), Integer.toString(b))); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using FFT. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Integer.parseInt(Multiplication.multiplyUsingLoopWithStringInput(Integer.toString(a), Integer.toString(b))); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using loop with string input. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingLoopWithIntegerInput(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using loop with int input. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + } + // positive * negative + a *= -1; + b *= -1; + { + long result = Multiplication.multiplyUsingLoop(a, b); + long check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using a loop. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingRecursion(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using recursion. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingShift(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using shifts. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingLogs(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using logs. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Integer.parseInt(Multiplication.multiplyUsingFFT(Integer.toString(a), Integer.toString(b))); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using FFT. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Integer.parseInt(Multiplication.multiplyUsingLoopWithStringInput(Integer.toString(a), Integer.toString(b))); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using loop with string input. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingLoopWithIntegerInput(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using loop with int input. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + } + // negative * negative + a *= -1; + { + long result = Multiplication.multiplyUsingLoop(a, b); + long check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using a loop. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingRecursion(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using recursion. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingShift(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using shifts. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingLogs(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using logs. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Integer.parseInt(Multiplication.multiplyUsingFFT(Integer.toString(a), Integer.toString(b))); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using FFT. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Integer.parseInt(Multiplication.multiplyUsingLoopWithStringInput(Integer.toString(a), Integer.toString(b))); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using loop with string input. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Multiplication.multiplyUsingLoopWithIntegerInput(a, b); + check = Multiplication.multiplication(a, b); + assertTrue("Multiplication using loop with int input. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + } + } + + @Test + public void division() { + int a = nextRandomInt(MIN, MAX); + int b = nextRandomInt(a, MAX); + long result = Division.divisionUsingLoop(a, b); + long check = Division.division(a, b); + assertTrue("Division using a loop. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Division.divisionUsingRecursion(a, b); + check = Division.division(a, b); + assertTrue("Division using recursion. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Division.divisionUsingShift(a, b); + check = Division.division(a, b); + assertTrue("Division using shifts. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Division.divisionUsingLogs(a, b); + check = Division.division(a, b); + assertTrue("Division using logs. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + + result = Division.divisionUsingMultiplication(a, b); + check = Division.division(a, b); + assertTrue("Division using multiplication. a=" + a + " b=" + b + " result=" + result + " check=" + check, (result == check)); + } + + @Test + public void knapsack() { + int[] values = {7, 4, 8, 6, 2, 5}; + int[] weights = {2, 3, 5, 4, 2, 3}; + int capacity = 9; + int[] result = Knapsack.zeroOneKnapsack(values, weights, capacity); + int[] check = new int[]{5, 3, 0}; + for (int i = 0; i < result.length; i++) { + int r = result[i]; + int c = check[i]; + assertTrue("Knapsack problem. expected=" + c + " got=" + r, r == c); + } + } + + @Test + public void chebyshevDistance() { + long[] point1 = new long[]{1, 2}; + long[] point2 = new long[]{7, 8}; + long result = Distance.chebyshevDistance(point1, point2); + long expected = 6; + assertTrue("Chebyshev Distance error. expected=" + expected + " got=" + result, (result == expected)); + } + + @Test + public void squaredDistance() { + int x1 = 1; + int y1 = 2; + int x2 = 7; + int y2 = 8; + double result = Distance.squaredDistance(x1, y1, x2, y2); + double expected = 72.0; + assertTrue("squaredDistance error. expected=" + expected + " got=" + result, (result == expected)); + } + + @Test + public void euclideanDistance() { + int x1 = 1; + int y1 = 2; + int x2 = 7; + int y2 = 8; + double result = Distance.euclideanDistance(x1, y1, x2, y2); + double expected = 8.48528137423857; + assertTrue("squaredDistance error. expected=" + expected + " got=" + result, (result == expected)); + } + + @Test + public void getPrimeFactorization() { + int number = 234; + Map factorization = Primes.getPrimeFactorization(number); + Map check = new HashMap(); + { + check.put(2l, 1L); + check.put(3l, 2L); + check.put(13L, 1L); + } + for (Long k : factorization.keySet()) { + Long f = factorization.get(k); + Long c = check.get(k); + assertTrue("PrimeFactorization error. expected=" + c + " got=" + f, (c == f)); + } + } + + @Test + public void isPrime() { + int number = 1234; + boolean isPrime = Primes.isPrime(number); + assertFalse("isPrime error. isPrime=" + isPrime, isPrime); + + number = 7919; + isPrime = Primes.isPrime(number); + assertTrue("isPrime error. isPrime=" + isPrime, isPrime); + } + + @Test + public void sieveOfEratosthenes() { + int number = 1; + boolean isPrime = Primes.sieveOfEratosthenes(number); + assertFalse("Sieve Of Eratosthenes error.", isPrime); + + number = 31; + isPrime = Primes.sieveOfEratosthenes(number); + assertTrue("Sieve Of Eratosthenes error.", isPrime); + + number = 64; + isPrime = Primes.sieveOfEratosthenes(number); + assertFalse("Sieve Of Eratosthenes error.", isPrime); + + number = 4177; + isPrime = Primes.sieveOfEratosthenes(number); + assertTrue("Sieve Of Eratosthenes error.", isPrime); + + number = 4178; + isPrime = Primes.sieveOfEratosthenes(number); + assertFalse("Sieve Of Eratosthenes error.", isPrime); + + number = 7919; + isPrime = Primes.sieveOfEratosthenes(number); + assertTrue("Sieve Of Eratosthenes error.", isPrime); + + number = 556; + isPrime = Primes.sieveOfEratosthenes(number); + assertFalse("Sieve Of Eratosthenes error.", isPrime); + + number = 6091; + isPrime = Primes.sieveOfEratosthenes(number); + assertTrue("Sieve Of Eratosthenes error.", isPrime); + + number = 6090; + isPrime = Primes.sieveOfEratosthenes(number); + assertFalse("Sieve Of Eratosthenes error.", isPrime); + + number = 6089; + isPrime = Primes.sieveOfEratosthenes(number); + assertTrue("Sieve Of Eratosthenes error.", isPrime); + } + + @Test + public void millerRabin() { + final List primeNumbers = Arrays.asList(2, 3, 7, 23, 2179, 25657, 34123); + final List compositeNumbers = Arrays.asList(4, 9, 27, 2457, 26575, 34121); + + for (int prime : primeNumbers) + assertTrue("Miller-Rabin test error. " + prime, Primes.millerRabinTest(prime)); + + for (int composite : compositeNumbers) + assertFalse("Miller-Rabin test error. " + composite, Primes.millerRabinTest(composite)); + } +} + diff --git a/test/com/jwetherell/algorithms/mathematics/test/ModularArithmeticTest.java b/test/com/jwetherell/algorithms/mathematics/test/ModularArithmeticTest.java new file mode 100644 index 00000000..30c1acbc --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/test/ModularArithmeticTest.java @@ -0,0 +1,129 @@ +package com.jwetherell.algorithms.mathematics.test; + +import com.jwetherell.algorithms.mathematics.Modular; +import org.junit.Test; +import static org.junit.Assert.*; + +public class ModularArithmeticTest { + + @Test + public void sumTest() { + assertEquals(4, Modular.add(-3, 22, 5)); + + assertEquals(Long.MAX_VALUE-2, Modular.add(Long.MAX_VALUE-1, Long.MAX_VALUE-1, Long.MAX_VALUE)); + + assertEquals(2, Modular.add(1-Long.MAX_VALUE, 1-Long.MAX_VALUE, Long.MAX_VALUE)); + + assertEquals(0, Modular.add(Long.MAX_VALUE/2, Long.MAX_VALUE/2 + 1, Long.MAX_VALUE)); + + assertEquals(0, Modular.add(-1000, -10000000, 10)); + + boolean exception = true; + try { + Modular.add(1, 1, 0); + exception = false; + } catch (IllegalArgumentException e) { + // ignore + } + assertTrue("Exception expected", exception); + } + + @Test + public void subtractTest() { + assertEquals(0, Modular.subtract(-22, 3, 5)); + + assertEquals(Long.MAX_VALUE-1, Modular.subtract(Long.MAX_VALUE-2, Long.MAX_VALUE-1, Long.MAX_VALUE)); + + assertEquals(Long.MAX_VALUE-1, Modular.subtract(1-Long.MAX_VALUE, 2, Long.MAX_VALUE)); + + assertEquals(0, Modular.subtract(-1000, -10000000, 10)); + + boolean exception = true; + try { + Modular.subtract(1, 1, 0); + exception = false; + } catch (IllegalArgumentException e) { + // ignore + } + assertTrue("Exception expected", exception); + } + + @Test + public void multiplyTest() { + assertEquals(10, Modular.multiply(Long.MAX_VALUE-2, Long.MAX_VALUE-5, Long.MAX_VALUE)); + + assertEquals(3, Modular.multiply(-5, -7, 32)); + + boolean exception = true; + try { + Modular.multiply(1, 1, 0); + exception = false; + } catch (IllegalArgumentException e) { + // ignore + } + assertTrue("Exception expected", exception); + } + + @Test + public void powerTest() { + assertEquals(1, Modular.pow(3, 1000000006, 1000000007)); + + assertEquals(8, Modular.pow(2, 66, Long.MAX_VALUE)); + + assertEquals(1, Modular.pow(123, 0, 1111)); + + assertEquals(0, Modular.pow(0, 123, 2)); + + boolean exception = true; + try { + Modular.pow(5, 0, 5); + exception = false; + } catch (IllegalArgumentException e) { + // ignore + } + assertTrue("Exception expected", exception); + + exception = true; + try { + Modular.pow(5, -5, 5); + exception = false; + } catch (IllegalArgumentException e) { + // ignore + } + assertTrue("Exception expected", exception); + + exception = true; + try { + Modular.pow(5, 5, 0); + exception = false; + } catch (IllegalArgumentException e) { + // ignore + } + assertTrue("Exception expected", exception); + } + + @Test + public void divideTest() { + assertEquals(1, Modular.divide(7, 7, 125)); + + assertEquals(97, Modular.divide(Modular.multiply(97, 25, 1023), 25, 1023)); + + assertEquals(Long.MAX_VALUE-11, Modular.divide(Modular.multiply(Long.MAX_VALUE-11, Long.MAX_VALUE-12, Long.MAX_VALUE), Long.MAX_VALUE-12, Long.MAX_VALUE)); + + boolean exception = true; + try { + Modular.divide(11, 6, 120); + } catch (IllegalArgumentException e) { + // ignore + } + assertTrue("Exception expected", exception); + + exception = true; + try { + Modular.divide(2, 2, 0); + } catch (ArithmeticException e) { + // ignore + } + assertTrue("Exception expected", exception); + } +} diff --git a/test/com/jwetherell/algorithms/mathematics/test/PermutationsTest.java b/test/com/jwetherell/algorithms/mathematics/test/PermutationsTest.java new file mode 100644 index 00000000..b0c27053 --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/test/PermutationsTest.java @@ -0,0 +1,74 @@ +package com.jwetherell.algorithms.mathematics.test; + +import static org.junit.Assert.*; + +import java.util.LinkedList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.mathematics.Permutations; + +public class PermutationsTest { + + @Test + public void test1NumberOfPermutations() { + final Integer[] numbers = {1,2,3,4}; + final int expectedNumberOfPermutations = 24; + assertEquals(expectedNumberOfPermutations, (Permutations.getAllPermutations(numbers)).size()); + } + + @Test + public void test2NumberOfPermutations() { + final Integer[] numbers = {3,4,2}; + final int expectedNumberOfPermutations = 6; + assertEquals(expectedNumberOfPermutations, (Permutations.getAllPermutations(numbers)).size()); + } + + @Test + public void test3NumberOfPermutations() { + final Integer[] numbers = {3,4,2,5,4,9}; + final int expectedNumberOfPermutations = 720; + assertEquals(expectedNumberOfPermutations, (Permutations.getAllPermutations(numbers)).size()); + } + + @Test + public void testComparePermutations() { + final Integer[] numbers = {4,2}; + + final LinkedList firstPermutation = new LinkedList(); + firstPermutation.add(4); + firstPermutation.add(2); + + final LinkedList secondPermutation = new LinkedList(); + secondPermutation.add(2); + secondPermutation.add(4); + + final LinkedList> allPermutations = new LinkedList>(); + allPermutations.add(firstPermutation); + allPermutations.add(secondPermutation); + + final List> result = Permutations.getAllPermutations(numbers); + assertTrue("allPermutations="+allPermutations+" result="+result, allPermutations.equals(result)); + } + + @Test + public void testPermutation1() { + final String string = "abc"; + final String[] list = Permutations.permutations(string); + Assert.assertTrue(list[0].equals("abc")); + Assert.assertTrue(list[5].equals("cba")); + } + + @Test + public void testPermutation2() { + final String string = "abcd"; + final String[] list = Permutations.permutations(string); + Assert.assertTrue(list[0].equals("abcd")); + Assert.assertTrue(list[5].equals("adcb")); + Assert.assertTrue(list[11].equals("bdca")); + Assert.assertTrue(list[17].equals("cdba")); + Assert.assertTrue(list[23].equals("dcba")); + } +} diff --git a/test/com/jwetherell/algorithms/mathematics/test/RamerDouglasPeuckerTest.java b/test/com/jwetherell/algorithms/mathematics/test/RamerDouglasPeuckerTest.java new file mode 100644 index 00000000..08f1f71f --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/test/RamerDouglasPeuckerTest.java @@ -0,0 +1,55 @@ +package com.jwetherell.algorithms.mathematics.test; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.jwetherell.algorithms.mathematics.RamerDouglasPeucker; + +public class RamerDouglasPeuckerTest { + + @Test + public void test1() { + final List list = new ArrayList(); + list.add(new Double[]{3.14d, 5.2d}); + list.add(new Double[]{5.7d, 8.1d}); + list.add(new Double[]{4.6d, -1.3d}); + + final List simplified = RamerDouglasPeucker.douglasPeucker(list, 5.0d); + final List expected = new ArrayList(); + expected.add(list.get(0)); + expected.add(list.get(2)); + Assert.assertTrue(simplified.size()==2 && simplified.equals(expected)); + } + + @Test + public void test2() { + final List list = new ArrayList(); + list.add(new Double[]{0.0d,0.0d}); + list.add(new Double[]{2.5d, 3.0d}); + list.add(new Double[]{5.0d, 0.0d}); + + final List simplified = RamerDouglasPeucker.douglasPeucker(list, 2.9d); + final List expected = new ArrayList(); + expected.add(list.get(0)); + expected.add(list.get(1)); + expected.add(list.get(2)); + Assert.assertTrue(simplified.size()==3 && simplified.equals(expected)); + } + + @Test + public void test3() { + final List list = new ArrayList(); + list.add(new Double[]{0.0d,0.0d}); + list.add(new Double[]{2.5d, 3.0d}); + list.add(new Double[]{5.0d, 0.0d}); + + final List simplified = RamerDouglasPeucker.douglasPeucker(list, 3.0d); + final List expected = new ArrayList(); + expected.add(list.get(0)); + expected.add(list.get(2)); + Assert.assertTrue(simplified.size()==2 && simplified.equals(expected)); + } +} diff --git a/test/com/jwetherell/algorithms/mathematics/timing/ExponentiationTiming.java b/test/com/jwetherell/algorithms/mathematics/timing/ExponentiationTiming.java new file mode 100644 index 00000000..20d254b3 --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/timing/ExponentiationTiming.java @@ -0,0 +1,24 @@ +package com.jwetherell.algorithms.mathematics.timing; + +import com.jwetherell.algorithms.mathematics.Exponentiation; + +/** + * Notice that 2^1000 is out of integer range so returned result is not correct. + * It is a reason why exponentiation modulo is useful. + * But it does not matter when you want to compare speed of these two algorithms. + */ +public class ExponentiationTiming { + public static void main(String[] args) { + System.out.println("Calculating a power using a recursive function."); + long before = System.nanoTime(); + Exponentiation.recursiveExponentiation(2, 1000); + long after = System.nanoTime(); + System.out.println("Computed in " + (after - before) + " ns"); + + System.out.println("Calculating a power using a fast recursive function."); + before = System.nanoTime(); + Exponentiation.fastRecursiveExponentiation(2, 1000); + after = System.nanoTime(); + System.out.println("Computed in " + (after - before) + " ns"); + } +} diff --git a/test/com/jwetherell/algorithms/mathematics/timing/GCDTiming.java b/test/com/jwetherell/algorithms/mathematics/timing/GCDTiming.java new file mode 100644 index 00000000..ca875cdd --- /dev/null +++ b/test/com/jwetherell/algorithms/mathematics/timing/GCDTiming.java @@ -0,0 +1,37 @@ +package com.jwetherell.algorithms.mathematics.timing; + +import java.text.DecimalFormat; + +import com.jwetherell.algorithms.mathematics.GreatestCommonDivisor; + +public class GCDTiming { + + private static final DecimalFormat FORMAT = new DecimalFormat("#.######"); + + public static void main(String[] args) { + // Euclidean + { + System.out.println("Euclid's Greatest Common Divisor."); + long x = 1989; + long y = 867; + long before = System.nanoTime(); + GreatestCommonDivisor.gcdUsingEuclides(x, y); + long after = System.nanoTime(); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); + System.gc(); + } + // Recursion + { + System.out.println("Recursive's Greatest Common Divisor."); + long x = 1989; + long y = 867; + long before = System.nanoTime(); + GreatestCommonDivisor.gcdUsingRecursion(x, y); + long after = System.nanoTime(); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); + System.gc(); + } + } +} diff --git a/src/com/jwetherell/algorithms/Mathematics.java b/test/com/jwetherell/algorithms/mathematics/timing/MathematicsTiming.java similarity index 72% rename from src/com/jwetherell/algorithms/Mathematics.java rename to test/com/jwetherell/algorithms/mathematics/timing/MathematicsTiming.java index cc265c58..9420b70f 100644 --- a/src/com/jwetherell/algorithms/Mathematics.java +++ b/test/com/jwetherell/algorithms/mathematics/timing/MathematicsTiming.java @@ -1,20 +1,19 @@ -package com.jwetherell.algorithms; +package com.jwetherell.algorithms.mathematics.timing; import java.text.DecimalFormat; import com.jwetherell.algorithms.mathematics.Division; -import com.jwetherell.algorithms.mathematics.Knapsack; import com.jwetherell.algorithms.mathematics.Multiplication; -public class Mathematics { +public class MathematicsTiming { private static final DecimalFormat FORMAT = new DecimalFormat("#.######"); public static void main(String[] args) { // MULTIPLICATION { - int a = 12; - int b = 14; + final int a = 1200; + final int b = 1400; System.out.println("Multiplication using a loop."); long before = System.nanoTime(); long result = Multiplication.multiplyUsingLoop(a, b); @@ -22,8 +21,6 @@ public static void main(String[] args) { long check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -34,8 +31,6 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -46,8 +41,6 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -58,16 +51,44 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using FFT."); + before = System.nanoTime(); + result = Long.parseLong(Multiplication.multiplyUsingFFT(String.valueOf(a), String.valueOf(b))); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using loop, String input."); + before = System.nanoTime(); + result = Long.parseLong(Multiplication.multiplyUsingLoopWithStringInput(String.valueOf(a), String.valueOf(b))); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using loop, Integer input."); + before = System.nanoTime(); + result = Multiplication.multiplyUsingLoopWithIntegerInput(a, b); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } { - int a = -74; - int b = 62; + final int a = -7400; + final int b = 6200; System.out.println("Multiplication using a loop."); long before = System.nanoTime(); long result = Multiplication.multiplyUsingLoop(a, b); @@ -75,8 +96,6 @@ public static void main(String[] args) { long check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -87,8 +106,6 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -99,8 +116,6 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -111,16 +126,44 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using FFT."); + before = System.nanoTime(); + result = Long.parseLong(Multiplication.multiplyUsingFFT(String.valueOf(a), String.valueOf(b))); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using loop, String input."); + before = System.nanoTime(); + result = Long.parseLong(Multiplication.multiplyUsingLoopWithStringInput(String.valueOf(a), String.valueOf(b))); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using loop, Integer input."); + before = System.nanoTime(); + result = Multiplication.multiplyUsingLoopWithIntegerInput(a, b); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } { - int a = 84; - int b = -79; + final int a = 8400; + final int b = -2900; System.out.println("Multiplication using a loop."); long before = System.nanoTime(); long result = Multiplication.multiplyUsingLoop(a, b); @@ -128,8 +171,6 @@ public static void main(String[] args) { long check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -140,8 +181,6 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -152,8 +191,6 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -164,16 +201,44 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using FFT."); + before = System.nanoTime(); + result = Long.parseLong(Multiplication.multiplyUsingFFT(String.valueOf(a), String.valueOf(b))); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using loop, String input."); + before = System.nanoTime(); + result = Long.parseLong(Multiplication.multiplyUsingLoopWithStringInput(String.valueOf(a), String.valueOf(b))); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using loop, Integer input."); + before = System.nanoTime(); + result = Multiplication.multiplyUsingLoopWithIntegerInput(a, b); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } { - int a = -92; - int b = -87; + final int a = -9200; + final int b = -3700; System.out.println("Multiplication using a loop."); long before = System.nanoTime(); long result = Multiplication.multiplyUsingLoop(a, b); @@ -181,8 +246,6 @@ public static void main(String[] args) { long check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -193,8 +256,6 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -205,8 +266,6 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -217,8 +276,36 @@ public static void main(String[] args) { check = Multiplication.multiplication(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "x" + b + "=" + result); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using FFT."); + before = System.nanoTime(); + result = Long.parseLong(Multiplication.multiplyUsingFFT(String.valueOf(a), String.valueOf(b))); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using loop, String input."); + before = System.nanoTime(); + result = Long.parseLong(Multiplication.multiplyUsingLoopWithStringInput(String.valueOf(a), String.valueOf(b))); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Multiplication using loop, Integer input."); + before = System.nanoTime(); + result = Multiplication.multiplyUsingLoopWithIntegerInput(a, b); + after = System.nanoTime(); + check = Multiplication.multiplication(a, b); + if (result != check) + System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); @@ -226,8 +313,8 @@ public static void main(String[] args) { // DIVISION { - int a = 9; - int b = 3; + final int a = 9; + final int b = 3; System.out.println("Division using a loop."); long before = System.nanoTime(); long result = Division.divisionUsingLoop(a, b); @@ -235,8 +322,6 @@ public static void main(String[] args) { long check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -247,8 +332,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -259,8 +342,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -271,8 +352,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -283,16 +362,14 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } { - int a = -54; - int b = 6; + final int a = -54; + final int b = 6; System.out.println("Division using a loop."); long before = System.nanoTime(); long result = Division.divisionUsingLoop(a, b); @@ -300,8 +377,6 @@ public static void main(String[] args) { long check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -312,8 +387,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -324,8 +397,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -336,8 +407,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -348,16 +417,14 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } { - int a = 98; - int b = -7; + final int a = 98; + final int b = -7; System.out.println("Division using a loop."); long before = System.nanoTime(); long result = Division.divisionUsingLoop(a, b); @@ -365,8 +432,6 @@ public static void main(String[] args) { long check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -377,8 +442,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -389,8 +452,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -401,8 +462,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -413,16 +472,14 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } { - int a = -568; - int b = -15; + final int a = -568; + final int b = -15; System.out.println("Division using a loop."); long before = System.nanoTime(); long result = Division.divisionUsingLoop(a, b); @@ -430,8 +487,6 @@ public static void main(String[] args) { long check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -442,8 +497,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -454,8 +507,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -466,8 +517,6 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.gc(); @@ -478,36 +527,9 @@ public static void main(String[] args) { check = Division.division(a, b); if (result != check) System.out.println("ERROR with a=" + a + " b=" + b + " result=" + result + " check=" + check); - else - System.out.println(a + "/" + b + "=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } - - { - int[] values = { 7, 4, 8, 6, 2, 5 }; - int[] weights = { 2, 3, 5, 4, 2, 3 }; - int capacity = 9; - System.out.println("Knapsack problem."); - long before = System.nanoTime(); - int[] result = Knapsack.zeroOneKnapsack(values, weights, capacity); - long after = System.nanoTime(); - System.out.println("result=" + getIntegerString(result)); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); - System.gc(); - } - } - - private static final String getIntegerString(int[] result) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < result.length; i++) { - int v = result[i]; - builder.append(v); - if (i != result.length - 1) - builder.append(", "); - } - return builder.toString(); } - } diff --git a/test/com/jwetherell/algorithms/numbers/test/Numbers.java b/test/com/jwetherell/algorithms/numbers/test/Numbers.java new file mode 100644 index 00000000..dcf087ab --- /dev/null +++ b/test/com/jwetherell/algorithms/numbers/test/Numbers.java @@ -0,0 +1,108 @@ +package com.jwetherell.algorithms.numbers.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Random; + +import org.junit.Test; + +import com.jwetherell.algorithms.numbers.Integers; +import com.jwetherell.algorithms.numbers.Longs; + +public class Numbers { + + private static final Random RANDOM = new Random(); + + @Test + public void testPowerOf2() { + int is = 256; + int isNot = 1673; + + boolean isPowOf2is = Integers.powerOfTwoUsingBits(is); + boolean isPowOf2isNot = Integers.powerOfTwoUsingBits(isNot); + assertTrue("Is power of 2 using bits error", isPowOf2is); + assertFalse("Is power of 2 using bits error", isPowOf2isNot); + + isPowOf2is = Integers.powerOfTwoUsingLog(is); + isPowOf2isNot = Integers.powerOfTwoUsingLog(isNot); + assertTrue("Is power of 2 using logarithm error", isPowOf2is); + assertFalse("Is power of 2 using logarithm error", isPowOf2isNot); + + isPowOf2is = Integers.powerOfTwoUsingLoop(is); + isPowOf2isNot = Integers.powerOfTwoUsingLoop(isNot); + assertTrue("Is power of 2 using loops error", isPowOf2is); + assertFalse("Is power of 2 using loops error", isPowOf2isNot); + + isPowOf2is = Integers.powerOfTwoUsingRecursion(is); + isPowOf2isNot = Integers.powerOfTwoUsingRecursion(isNot); + assertTrue("Is power of 2 using recursion error", isPowOf2is); + assertFalse("Is power of 2 using recursion error", isPowOf2isNot); + } + + @Test + public void testIntegerToBinaryString() { + int a = Math.abs(RANDOM.nextInt()); + String check = Integer.toBinaryString(a); + + String result1 = Integers.toBinaryUsingBigDecimal(a); + assertTrue("toBinary using BigDecimal error. random="+a+" expected=\n"+check+"\ngot=\n"+result1+"\n", (check.equals(result1))); + + String result2 = Integers.toBinaryUsingDivideAndDouble(a); + assertTrue("toBinary using BigDecimal error. random="+a+" expected=\n"+check+"\ngot="+result2+"\n", (check.equals(result2))); + + String result3 = Integers.toBinaryUsingDivideAndModulus(a); + assertTrue("toBinary using BigDecimal error. random="+a+" expected=\n"+check+"\ngot="+result3+"\n", (check.equals(result3))); + + String result4 = Integers.toBinaryUsingShiftsAndModulus(a); + assertTrue("toBinary using BigDecimal error. random="+a+" expected=\n"+check+"\ngot="+result4+"\n", (check.equals(result4))); + } + + @Test + public void testLongToBinaryString() { + long a = Math.abs(RANDOM.nextLong()); + String check = Long.toBinaryString(a); + + String result1 = Longs.toBinaryUsingBigDecimal(a); + assertTrue("toBinary using BigDecimal error. random="+a+" expected=\n"+check+"\ngot=\n"+result1+"\n", (check.equals(result1))); + + String result2 = Longs.toBinaryUsingDivideAndModulus(a); + assertTrue("toBinary using BigDecimal error. random="+a+" expected=\n"+check+"\ngot=\n"+result2+"\n", (check.equals(result2))); + + String result3 = Longs.toBinaryUsingShiftsAndModulus(a); + assertTrue("toBinary using BigDecimal error. random="+a+" expected=\n"+check+"\ngot=\n"+result3+"\n", (check.equals(result3))); + } + + @Test + public void testToEnglish() { + int a = -1001; + String check = "negative one-thousand one"; + String english = Integers.toEnglish(a); + assertTrue("toEnglish error. a="+a+" expected="+check+" got="+english, (check.equals(english))); + + a = -1; + check = "negative one"; + english = Integers.toEnglish(a); + assertTrue("toEnglish error. a="+a+" expected="+check+" got="+english, (check.equals(english))); + + a = 0; + check = "zero"; + english = Integers.toEnglish(a); + assertTrue("toEnglish error. a="+a+" expected="+check+" got="+english, (check.equals(english))); + + a = 199; + check = "one-hundred ninety-nine"; + english = Integers.toEnglish(a); + assertTrue("toEnglish error. a="+a+" expected="+check+" got="+english, (check.equals(english))); + + a = Integer.MAX_VALUE; // 2,147,483,647 + check = "two-billion one-hundred forty-seven-million four-hundred eighty-three-thousand six-hundred forty-seven"; + english = Integers.toEnglish(a); + assertTrue("toEnglish error. a="+a+" expected="+check+" got="+english, (check.equals(english))); + + a = Integer.MIN_VALUE+1; // -2,147,483,647 + check = "negative two-billion one-hundred forty-seven-million four-hundred eighty-three-thousand six-hundred forty-seven"; + english = Integers.toEnglish(a); + assertTrue("toEnglish error. a="+a+" expected="+check+" got="+english, (check.equals(english))); + } +} diff --git a/src/com/jwetherell/algorithms/Numbers.java b/test/com/jwetherell/algorithms/numbers/timing/NumbersTiming.java similarity index 62% rename from src/com/jwetherell/algorithms/Numbers.java rename to test/com/jwetherell/algorithms/numbers/timing/NumbersTiming.java index feb6d594..d23a1e2d 100644 --- a/src/com/jwetherell/algorithms/Numbers.java +++ b/test/com/jwetherell/algorithms/numbers/timing/NumbersTiming.java @@ -1,11 +1,11 @@ -package com.jwetherell.algorithms; +package com.jwetherell.algorithms.numbers.timing; import java.text.DecimalFormat; import com.jwetherell.algorithms.numbers.Integers; import com.jwetherell.algorithms.numbers.Longs; -public class Numbers { +public class NumbersTiming { private static final DecimalFormat FORMAT = new DecimalFormat("#.######"); @@ -15,84 +15,68 @@ public static void main(String[] args) { int a = Integer.MAX_VALUE; System.out.println("Integer to binary string using division and modulus."); long before = System.nanoTime(); - String result = Integers.toBinaryUsingDivideAndModulus(a); + Integers.toBinaryUsingDivideAndModulus(a); long after = System.nanoTime(); - System.out.println("a=" + a + " " + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Integer to binary string using shifts and modulus."); before = System.nanoTime(); - result = Integers.toBinaryUsingShiftsAndModulus(a); + Integers.toBinaryUsingShiftsAndModulus(a); after = System.nanoTime(); - System.out.println("a=" + a + " " + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Integer to binary string using BigDecimal."); before = System.nanoTime(); - result = Integers.toBinaryUsingBigDecimal(a); + Integers.toBinaryUsingBigDecimal(a); after = System.nanoTime(); - System.out.println("a=" + a + " " + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Integer to binary string using divide and double."); before = System.nanoTime(); - result = Integers.toBinaryUsingDivideAndDouble(a); + Integers.toBinaryUsingDivideAndDouble(a); after = System.nanoTime(); - System.out.println("a=" + a + " " + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); - System.gc(); - - System.out.println("Euclid's Greatest Common Divisor."); - int x = 1989; - int y = 867; - before = System.nanoTime(); - int gcd = Integers.euclidsGreatestCommonDivsor(x, y); - after = System.nanoTime(); - System.out.println("x=" + x + " " + "y=" + y + " " + gcd); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); a = (int) Math.pow(2, 30); System.out.println("Power of 2 using loop."); before = System.nanoTime(); - boolean isPowOf2 = Integers.powerOfTwoUsingLoop(a); + Integers.powerOfTwoUsingLoop(a); after = System.nanoTime(); - System.out.println("a=" + a + " is a power of 2? " + isPowOf2); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Power of 2 using recursion."); before = System.nanoTime(); - isPowOf2 = Integers.powerOfTwoUsingRecursion(a); + Integers.powerOfTwoUsingRecursion(a); after = System.nanoTime(); - System.out.println("a=" + a + " is a power of 2? " + isPowOf2); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Power of 2 using logarithm."); before = System.nanoTime(); - isPowOf2 = Integers.powerOfTwoUsingLog(a); + Integers.powerOfTwoUsingLog(a); after = System.nanoTime(); - System.out.println("a=" + a + " is a power of 2? " + isPowOf2); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Power of 2 using bits."); before = System.nanoTime(); - isPowOf2 = Integers.powerOfTwoUsingBits(a); + Integers.powerOfTwoUsingBits(a); after = System.nanoTime(); - System.out.println("a=" + a + " is a power of 2? " + isPowOf2); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); - System.gc(); - - System.out.println("Integer to English"); - for (int i=-1001; i<=1001; i++) { - System.out.println(Integers.toEnglish(i)); - } System.out.println(); + System.gc(); } // Longs @@ -100,26 +84,26 @@ public static void main(String[] args) { long a = Long.MAX_VALUE; System.out.println("Long to binary string using division and modulus."); long before = System.nanoTime(); - String result = Longs.toBinaryUsingDivideAndModulus(a); + Longs.toBinaryUsingDivideAndModulus(a); long after = System.nanoTime(); - System.out.println("a=" + a + " " + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Long to binary string using shifts and modulus."); before = System.nanoTime(); - result = Longs.toBinaryUsingShiftsAndModulus(a); + Longs.toBinaryUsingShiftsAndModulus(a); after = System.nanoTime(); - System.out.println("a=" + a + " " + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Long to binary string using BigDecimal."); before = System.nanoTime(); - result = Longs.toBinaryUsingBigDecimal(a); + Longs.toBinaryUsingBigDecimal(a); after = System.nanoTime(); - System.out.println("a=" + a + " " + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); } } diff --git a/test/com/jwetherell/algorithms/search/test/Search.java b/test/com/jwetherell/algorithms/search/test/Search.java new file mode 100644 index 00000000..4759709f --- /dev/null +++ b/test/com/jwetherell/algorithms/search/test/Search.java @@ -0,0 +1,138 @@ +package com.jwetherell.algorithms.search.test; + +import static org.junit.Assert.assertTrue; + +import com.jwetherell.algorithms.search.*; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; + +public class Search { + + private static final int SIZE = 9999; + private static final int offset = 123; + + private static int[] sorted = new int[SIZE]; + + static { + for (int i = offset; i < offset + sorted.length; i++) { + sorted[i - offset] = i; + } + } + + private static int valueIndex = SIZE - (SIZE / 4); + private static int valueInArray = sorted[valueIndex]; + private static int valueNotInArray = sorted[SIZE - 1] + offset; + + @Test + public void testBruteForceSearch() { + int index = LinearSearch.find(valueInArray, sorted); + assertTrue("Brute force error. expected=" + valueIndex + " got=" + index, (index == valueIndex)); + index = LinearSearch.find(valueNotInArray, sorted); + assertTrue("Brute force error. expected=" + Integer.MAX_VALUE + " got=" + index, (index == Integer.MAX_VALUE)); + } + + @Test + public void testBinarySearch() { + int index = BinarySearch.find(valueInArray, sorted, false); + assertTrue("Brute force error. expected=" + valueIndex + " got=" + index, (index == valueIndex)); + index = BinarySearch.find(valueNotInArray, sorted, false); + assertTrue("Brute force error. expected=" + Integer.MAX_VALUE + " got=" + index, (index == Integer.MAX_VALUE)); + } + + @Test + public void testOptimizedBinarySearch() { + int index = BinarySearch.find(valueInArray, sorted, true); + assertTrue("Brute force error. expected=" + valueIndex + " got=" + index, (index == valueIndex)); + index = BinarySearch.find(valueNotInArray, sorted, true); + assertTrue("Brute force error. expected=" + Integer.MAX_VALUE + " got=" + index, (index == Integer.MAX_VALUE)); + } + + @Test + public void testInterpolationSearch() { + int index = InterpolationSearch.find(valueInArray, sorted); + assertTrue("Brute force error. expected=" + valueIndex + " got=" + index, (index == valueIndex)); + index = InterpolationSearch.find(valueNotInArray, sorted); + assertTrue("Brute force error. expected=" + Integer.MAX_VALUE + " got=" + index, (index == Integer.MAX_VALUE)); + } + + @Test + public void testQuickSelect() { + int index = QuickSelect.find(valueInArray, sorted); + assertTrue("Brute force error. expected=" + valueIndex + " got=" + index, (index == valueIndex)); + index = QuickSelect.find(valueNotInArray, sorted); + assertTrue("Brute force error. expected=" + Integer.MAX_VALUE + " got=" + index, (index == Integer.MAX_VALUE)); + } + + @Test + public void testLowerBound() { + ArrayList sequences = new ArrayList(); + ArrayList soughtElements = new ArrayList(); + ArrayList resultIndexes = new ArrayList(); + + sequences.add(new int[]{0, 1, 2, 3, 3, 3, 3, 8, 9}); + soughtElements.add(-1); + resultIndexes.add(0); + + sequences.add(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + soughtElements.add(4); + resultIndexes.add(4); + + sequences.add(new int[]{0, 1, 2, 3, 3, 3, 3, 3, 8, 9}); + soughtElements.add(3); + resultIndexes.add(3); + + sequences.add(new int[]{0, 1, 2, 3, 3, 3, 3, 8, 9}); + soughtElements.add(3); + resultIndexes.add(3); + + + sequences.add(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + soughtElements.add(44); + resultIndexes.add(10); + + assertTrue("Lower bound error. Sequences size=" + sequences.size() + " sough elements size=" + soughtElements.size() + " results size:" + resultIndexes.size(), sequences.size() == resultIndexes.size() && sequences.size() == soughtElements.size()); + + for (int i = 0; i < sequences.size(); ++i) { + int resultIndex = LowerBound.lowerBound(sequences.get(i), sequences.get(i).length, soughtElements.get(i)); + assertTrue("Lower bound error. Sequence=" + Arrays.toString(sequences.get(i)) + " sought element=" + soughtElements.get(i) + " expected result=" + resultIndexes.get(i) + " got result=" + resultIndex, resultIndex == resultIndexes.get(i)); + } + + } + + @Test + public void testUpperBound() { + ArrayList sequences = new ArrayList(); + ArrayList soughtElements = new ArrayList(); + ArrayList resultIndexes = new ArrayList(); + + sequences.add(new int[]{0, 1, 2, 3, 3, 3, 3, 8, 9}); + soughtElements.add(-1); + resultIndexes.add(0); + + sequences.add(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + soughtElements.add(4); + resultIndexes.add(5); + + sequences.add(new int[]{0, 1, 2, 3, 3, 3, 3, 3, 8, 9}); + soughtElements.add(3); + resultIndexes.add(8); + + sequences.add(new int[]{0, 1, 2, 3, 3, 3, 3, 8, 9}); + soughtElements.add(3); + resultIndexes.add(7); + + sequences.add(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + soughtElements.add(44); + resultIndexes.add(10); + + assertTrue("Upper bound error. Sequences size=" + sequences.size() + " sough elements size=" + soughtElements.size() + " results size:" + resultIndexes.size(), sequences.size() == resultIndexes.size() && sequences.size() == soughtElements.size()); + + for (int i = 0; i < sequences.size(); ++i) { + int resultIndex = UpperBound.upperBound(sequences.get(i), sequences.get(i).length, soughtElements.get(i)); + assertTrue("Upper bound error. Sequence=" + Arrays.toString(sequences.get(i)) + " sought element=" + soughtElements.get(i) + " expected result=" + resultIndexes.get(i) + " got result=" + resultIndex, resultIndex == resultIndexes.get(i)); + } + + } +} diff --git a/src/com/jwetherell/algorithms/Search.java b/test/com/jwetherell/algorithms/search/timing/SearchTiming.java similarity index 50% rename from src/com/jwetherell/algorithms/Search.java rename to test/com/jwetherell/algorithms/search/timing/SearchTiming.java index b1be21b9..66d42e21 100644 --- a/src/com/jwetherell/algorithms/Search.java +++ b/test/com/jwetherell/algorithms/search/timing/SearchTiming.java @@ -1,4 +1,4 @@ -package com.jwetherell.algorithms; +package com.jwetherell.algorithms.search.timing; import java.text.DecimalFormat; @@ -7,7 +7,7 @@ import com.jwetherell.algorithms.search.InterpolationSearch; import com.jwetherell.algorithms.search.QuickSelect; -public class Search { +public class SearchTiming { private static final DecimalFormat FORMAT = new DecimalFormat("#.######"); private static final int SIZE = 9999; @@ -28,17 +28,15 @@ public static void main(String[] args) { int valueNotInArray = sorted[SIZE - 1] + offset; { - System.out.println("Brute Force."); + System.out.println("Linear Search."); long before = System.nanoTime(); - int result = LinearSearch.find(valueInArray, sorted); + LinearSearch.find(valueInArray, sorted); long after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Linear Search. found in " + FORMAT.format(after - before) + " ns"); before = System.nanoTime(); - result = LinearSearch.find(valueNotInArray, sorted); + LinearSearch.find(valueNotInArray, sorted); after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Linear Search. not found in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } @@ -46,15 +44,13 @@ public static void main(String[] args) { { System.out.println("Binary Search."); long before = System.nanoTime(); - int result = BinarySearch.find(valueInArray, sorted, false); + BinarySearch.find(valueInArray, sorted, false); long after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Binary Search. found in " + FORMAT.format(after - before) + " ns"); before = System.nanoTime(); - result = BinarySearch.find(valueNotInArray, sorted, false); + BinarySearch.find(valueNotInArray, sorted, false); after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Binary Search. not found in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } @@ -62,15 +58,13 @@ public static void main(String[] args) { { System.out.println("Optimized Binary Search."); long before = System.nanoTime(); - int result = BinarySearch.find(valueInArray, sorted, true); + BinarySearch.find(valueInArray, sorted, true); long after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Optimized Binary Search. found in " + FORMAT.format(after - before) + " ns"); before = System.nanoTime(); - result = BinarySearch.find(valueNotInArray, sorted, true); + BinarySearch.find(valueNotInArray, sorted, true); after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Optimized Binary Search. not found in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } @@ -78,34 +72,29 @@ public static void main(String[] args) { { System.out.println("Interpolation Search."); long before = System.nanoTime(); - int result = InterpolationSearch.find(valueInArray, sorted); + InterpolationSearch.find(valueInArray, sorted); long after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Interpolation Search. found in " + FORMAT.format(after - before) + " ns"); before = System.nanoTime(); - result = InterpolationSearch.find(valueNotInArray, sorted); + InterpolationSearch.find(valueNotInArray, sorted); after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Interpolation Search. not found in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } { - System.out.println("Quick Select"); + System.out.println("Quick Select."); long before = System.nanoTime(); - int result = QuickSelect.find(valueInArray, sorted); + QuickSelect.find(valueInArray, sorted); long after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Quick Select. found in " + FORMAT.format(after - before) + " ns"); before = System.nanoTime(); - result = QuickSelect.find(valueNotInArray, sorted); + QuickSelect.find(valueNotInArray, sorted); after = System.nanoTime(); - System.out.println("result=" + result); - System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println("Quick Select. not found in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); } } - } diff --git a/test/com/jwetherell/algorithms/sequence/test/Sequences.java b/test/com/jwetherell/algorithms/sequence/test/Sequences.java new file mode 100644 index 00000000..bd3fa1d8 --- /dev/null +++ b/test/com/jwetherell/algorithms/sequence/test/Sequences.java @@ -0,0 +1,178 @@ +package com.jwetherell.algorithms.sequence.test; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import com.jwetherell.algorithms.sequence.LongestIncreasingSubsequence; +import com.jwetherell.algorithms.sequence.LongestPalindromicSubsequence; + +import org.junit.Test; + +import com.jwetherell.algorithms.sequence.FibonacciSequence; +import com.jwetherell.algorithms.sequence.LargestSumContiguousSubarray; +import com.jwetherell.algorithms.sequence.LongestCommonSubsequence; +import com.jwetherell.algorithms.sequence.SubsequenceCounter; +import com.jwetherell.algorithms.sequence.ArithmeticProgression; + +public class Sequences { + + @Test + public void testArithmeticProgression() { + // TOTAL OF A SEQUENCE OF NUMBERS + int start = 14; + int length = 10000; + int check = 50135000; + long result = ArithmeticProgression.sequenceTotalUsingLoop(start, length); + assertTrue("Sequence Total Using Loop error. result=" + result + " check=" + check, (result == check)); + + result = ArithmeticProgression.sequenceTotalUsingTriangularNumbers(start, length); + assertTrue("Sequence Total Using Triangular Numbers error. result=" + result + " check=" + check, (result == check)); + } + + @Test + public void testFibonacci() { + // COMPUTE FIBONACCI SEQUENCE + int element = 25; + int check = 75025; + long result = FibonacciSequence.fibonacciSequenceUsingLoop(element); + assertTrue("Fibonacci Sequence Using Loop error. result=" + result + " check=" + check, (result == check)); + + result = FibonacciSequence.fibonacciSequenceUsingRecursion(element); + assertTrue("Fibonacci Sequence Using Recursion error. result=" + result + " check=" + check, (result == check)); + + result = FibonacciSequence.fibonacciSequenceUsingMatrixMultiplication(element); + assertTrue("Fibonacci Sequence Using Matrix error. result=" + result + " check=" + check, (result == check)); + + result = FibonacciSequence.fibonacciSequenceUsingBinetsFormula(element); + assertTrue("Fibonacci Sequence Using Binet's formula error. result=" + result + " check=" + check, (result == check)); + } + + @Test(expected = IllegalArgumentException.class) + public void testFibonacciLoopExceptions() { + // COMPUTE FIBONACCI SEQUENCE + int element = 93; + FibonacciSequence.fibonacciSequenceUsingLoop(element); + } + + @Test(expected = IllegalArgumentException.class) + public void testFibonacciRecursionExceptions() { + // COMPUTE FIBONACCI SEQUENCE + int element = 93; + FibonacciSequence.fibonacciSequenceUsingRecursion(element); + } + + @Test(expected = IllegalArgumentException.class) + public void testFibonacciMatrixExceptions() { + // COMPUTE FIBONACCI SEQUENCE + int element = 93; + FibonacciSequence.fibonacciSequenceUsingMatrixMultiplication(element); + } + + @Test(expected = IllegalArgumentException.class) + public void testFibonacciBinetsExceptions() { + // COMPUTE FIBONACCI SEQUENCE + int element = 93; + FibonacciSequence.fibonacciSequenceUsingBinetsFormula(element); + } + + @Test + public void testLongestCommonSubSequences() { + // LONGEST COMMON SUBSEQUENCE + int resultLength = 2; + Set resultSequence = new HashSet(); + resultSequence.add("AC"); + resultSequence.add("GC"); + resultSequence.add("GA"); + char[] seq1 = new char[]{'G', 'A', 'C'}; + char[] seq2 = new char[]{'A', 'G', 'C', 'A', 'T'}; + LongestCommonSubsequence.MatrixPair pair = LongestCommonSubsequence.getLCS(seq1, seq2); + assertTrue("Longest common subsequence error. " + + "resultLength=" + resultLength + " seqLength=" + pair.getLongestSequenceLength() + " " + + "resultSequence=" + resultSequence + " sequence=" + pair.getLongestSequences(), + (resultLength == pair.getLongestSequenceLength() && + resultSequence.equals(pair.getLongestSequences()))); + + resultLength = 3; + resultSequence.clear(); + resultSequence.add("GAX"); + resultSequence.add("ACT"); + resultSequence.add("GCT"); + resultSequence.add("GAT"); + resultSequence.add("ACX"); + resultSequence.add("GCX"); + seq1 = new char[]{'G', 'A', 'C', 'V', 'X', 'T'}; + seq2 = new char[]{'A', 'G', 'C', 'A', 'T', 'X'}; + pair = LongestCommonSubsequence.getLCS(seq1, seq2); + assertTrue("Longest common subsequence error. " + + "resultLength=" + resultLength + " seqLength=" + pair.getLongestSequenceLength() + " " + + "resultSequence=" + resultSequence + " sequence=" + pair.getLongestSequences(), + (resultLength == pair.getLongestSequenceLength() && + resultSequence.equals(pair.getLongestSequences()))); + } + + @Test + public void testLongestIncreasingSubsequence() { + ArrayList sequences = new ArrayList(); + ArrayList sequencesLis = new ArrayList(); + + sequences.add(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + sequencesLis.add(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + + sequences.add(new int[]{0, 1, 2, 2, 2, 2, 2, 2, 9}); + sequencesLis.add(new int[]{0, 1, 2, 9}); + + sequences.add(new int[]{0, 1, 2, 2, 2, 2, 2, 2, 2, 9}); + sequencesLis.add(new int[]{0, 1, 2, 9}); + + sequences.add(new int[]{7, 7, 7, 7, 7, 7, 7, 7, 7, 7}); + sequencesLis.add(new int[]{7}); + + sequences.add(new int[]{8}); + sequencesLis.add(new int[]{8}); + + sequences.add(new int[]{172, 191, 179, 185, 188}); + sequencesLis.add(new int[]{172, 179, 185, 188}); + + sequences.add(new int[]{1, 2, 3, 1, 2, 3, 1, 2, 3}); + sequencesLis.add(new int[]{1, 2, 3}); + + sequences.add(new int[]{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}); + sequencesLis.add(new int[]{0, 2, 6, 9, 11, 15}); + + assertTrue("Longest increasing subsequence error. Sequences size=" + sequences.size() + " SequencesList size:" + sequencesLis.size(), sequences.size() == sequencesLis.size()); + + for (int i = 0; i < sequences.size(); ++i) { + int[] resultSequence = LongestIncreasingSubsequence.getLongestIncreasingSubsequence(sequences.get(i)); + assertTrue("Longest increasing subsequence error. Expected subsequence=" + Arrays.toString(sequencesLis.get(i)) + " result subsequence=" + Arrays.toString(resultSequence), Arrays.equals(resultSequence, sequencesLis.get(i))); + } + } + + @Test + public void testSubsequenceCount() { + final String a = "GeeksforGeeks"; + final String b = "Gks"; + final int result = SubsequenceCounter.getCount(a.toCharArray(), b.toCharArray()); + assertTrue("Subsequence Counter, expected="+4+" result="+result, result==4); + } + + @Test + public void testLargestSumContiguousSubarray() { + final int result = LargestSumContiguousSubarray.getLargestSumContiguousSubarray(new int[]{-2, 1, -3, 4, -1, 2, 1, -5, 4}); + assertTrue("Largest Sum of Contiguous Subarray, expected="+6+" result="+result, result==6); + final int result1 = LargestSumContiguousSubarray.getLargestSumContiguousSubarray(new int[]{-2, -1, -3, -4, -1, -2, -10, -5, -4}); + assertTrue("Largest Sum of Contiguous Subarray, expected="+-1+" result1="+result1, result1==-1); + } + + @Test + public void testLongestPalindromicSubsequence() { + final int result = LongestPalindromicSubsequence.getLongestPalindromeSubsequence("AABCDEBAZ"); + assertTrue("Longest Palindromic Subsequence, expected="+5+" result="+result, result==5); + final int result1 = LongestPalindromicSubsequence.getLongestPalindromeSubsequence("subsequence"); + assertTrue("Longest Palindromic Subsequence, expected="+3+" result1="+result1, result1==3); + } + +} diff --git a/test/com/jwetherell/algorithms/sequence/timing/SequencesTiming.java b/test/com/jwetherell/algorithms/sequence/timing/SequencesTiming.java new file mode 100644 index 00000000..551d3e50 --- /dev/null +++ b/test/com/jwetherell/algorithms/sequence/timing/SequencesTiming.java @@ -0,0 +1,66 @@ +package com.jwetherell.algorithms.sequence.timing; + +import java.text.DecimalFormat; + +import com.jwetherell.algorithms.sequence.FibonacciSequence; +import com.jwetherell.algorithms.sequence.ArithmeticProgression; + +public class SequencesTiming { + + private static final DecimalFormat FORMAT = new DecimalFormat("#.######"); + + public static void main(String[] args) { + { + // TOTAL OF A SEQUENCE OF NUMBERS + int start = 14; + int length = 10000; + System.out.println("Computing sequence total using a loop."); + long before = System.nanoTime(); + ArithmeticProgression.sequenceTotalUsingLoop(start, length); + long after = System.nanoTime(); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Computing sequence total using Triangular Numbers."); + before = System.nanoTime(); + ArithmeticProgression.sequenceTotalUsingTriangularNumbers(start, length); + after = System.nanoTime(); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); + System.gc(); + } + + { + // COMPUTE FIBONACCI SEQUENCE + System.out.println("Computing Fibonacci sequence total using a loop."); + int element = 25; + long before = System.nanoTime(); + FibonacciSequence.fibonacciSequenceUsingLoop(element); + long after = System.nanoTime(); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Computing Fibonacci sequence total using Recursion."); + before = System.nanoTime(); + FibonacciSequence.fibonacciSequenceUsingRecursion(element); + after = System.nanoTime(); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Computing Fibonacci sequence total using Matrix."); + before = System.nanoTime(); + FibonacciSequence.fibonacciSequenceUsingMatrixMultiplication(element); + after = System.nanoTime(); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.gc(); + + System.out.println("Computing Fibonacci sequence total using Binet's formula."); + before = System.nanoTime(); + FibonacciSequence.fibonacciSequenceUsingBinetsFormula(element); + after = System.nanoTime(); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); + System.gc(); + } + } +} diff --git a/test/com/jwetherell/algorithms/sorts/test/Sorts.java b/test/com/jwetherell/algorithms/sorts/test/Sorts.java new file mode 100644 index 00000000..226c29ee --- /dev/null +++ b/test/com/jwetherell/algorithms/sorts/test/Sorts.java @@ -0,0 +1,189 @@ +package com.jwetherell.algorithms.sorts.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Random; + +import org.junit.Test; + +import com.jwetherell.algorithms.sorts.AmericanFlagSort; +import com.jwetherell.algorithms.sorts.BubbleSort; +import com.jwetherell.algorithms.sorts.CountingSort; +import com.jwetherell.algorithms.sorts.HeapSort; +import com.jwetherell.algorithms.sorts.InsertionSort; +import com.jwetherell.algorithms.sorts.MergeSort; +import com.jwetherell.algorithms.sorts.QuickSort; +import com.jwetherell.algorithms.sorts.RadixSort; +import com.jwetherell.algorithms.sorts.ShellSort; + +public class Sorts { + + private static final Random RANDOM = new Random(); + private static final int SIZE = 10000; + + private static Integer[] unsorted = null; + private static Integer[] sorted = null; + private static Integer[] reverse = null; + + static { + unsorted = new Integer[SIZE]; + int i = 0; + while (i < unsorted.length) { + int j = RANDOM.nextInt(unsorted.length * 10); + unsorted[i++] = j; + } + + sorted = new Integer[SIZE]; + for (i = 0; i < sorted.length; i++) + sorted[i] = i; + + reverse = new Integer[SIZE]; + for (i = (reverse.length - 1); i >= 0; i--) + reverse[i] = (SIZE - 1) - i; + } + + @Test + public void testInsertionSorts() { + // Insertion sort + Integer[] result = InsertionSort.sort(unsorted.clone()); + assertTrue("Inerstion sort unsorted error. result="+print(result), check(result)); + result = InsertionSort.sort(sorted.clone()); + assertTrue("Inerstion sort sorted error. result="+print(result), check(result)); + result = InsertionSort.sort(reverse.clone()); + assertTrue("Inerstion sort reverse error. result="+print(result), check(result)); + } + + @Test + public void testBubbleSorts() { + // Bubble sort + Integer[] result = BubbleSort.sort(unsorted.clone()); + assertTrue("Bubble sort unsorted error. result="+print(result), check(result)); + result = BubbleSort.sort(sorted.clone()); + assertTrue("Bubble sort sorted error. result="+print(result), check(result)); + result = BubbleSort.sort(reverse.clone()); + assertTrue("Bubble sort reverse error. result="+print(result), check(result)); + } + + @Test + public void testShellsSorts() { + int[] shells = new int[] { 10, 5, 3, 1 }; + // Shell's sort + Integer[] result = ShellSort.sort(shells, unsorted.clone()); + assertTrue("Shell's sort unsorted error. result="+print(result), check(result)); + result = ShellSort.sort(shells, sorted.clone()); + assertTrue("Shell's sort sorted error. result="+print(result), check(result)); + result = ShellSort.sort(shells, reverse.clone()); + assertTrue("Shell's sort reverse error. result="+print(result), check(result)); + } + + @Test + public void testMergeSortsInPlace() { + // Merge sort + Integer[] result = MergeSort.sort(MergeSort.SPACE_TYPE.IN_PLACE, unsorted.clone()); + assertTrue("Merge sort unsorted error. result="+print(result), check(result)); + result = MergeSort.sort(MergeSort.SPACE_TYPE.IN_PLACE, sorted.clone()); + assertTrue("Merge sort sorted error. result="+print(result), check(result)); + result = MergeSort.sort(MergeSort.SPACE_TYPE.IN_PLACE, reverse.clone()); + assertTrue("merge sort reverse error. result="+print(result), check(result)); + } + + @Test + public void testMergeSortsNotInPlace() { + // Merge sort + Integer[] result = MergeSort.sort(MergeSort.SPACE_TYPE.NOT_IN_PLACE, unsorted.clone()); + assertTrue("Merge sort unsorted error. result="+print(result), check(result)); + result = MergeSort.sort(MergeSort.SPACE_TYPE.NOT_IN_PLACE, sorted.clone()); + assertTrue("Merge sort sorted error. result="+print(result), check(result)); + result = MergeSort.sort(MergeSort.SPACE_TYPE.NOT_IN_PLACE, reverse.clone()); + assertTrue("merge sort reverse error. result="+print(result), check(result)); + } + + @Test + public void testQuickSorts() { + // Quicksort + Integer[] result = QuickSort.sort(QuickSort.PIVOT_TYPE.FIRST, unsorted.clone()); + assertTrue("Quick sort pivot firt unsorted error. result="+print(result), check(result)); + result = QuickSort.sort(QuickSort.PIVOT_TYPE.FIRST, sorted.clone()); + assertTrue("Quick sort pivot firt sorted error. result="+print(result), check(result)); + result = QuickSort.sort(QuickSort.PIVOT_TYPE.FIRST, reverse.clone()); + assertTrue("Quick sort pivot firt reverse error. result="+print(result), check(result)); + result = QuickSort.sort(QuickSort.PIVOT_TYPE.MIDDLE, unsorted.clone()); + assertTrue("Quick sort pivot middle unsorted error. result="+print(result), check(result)); + result = QuickSort.sort(QuickSort.PIVOT_TYPE.MIDDLE, sorted.clone()); + assertTrue("Quick sort pivot middle sorted error. result="+print(result), check(result)); + result = QuickSort.sort(QuickSort.PIVOT_TYPE.MIDDLE, reverse.clone()); + assertTrue("Quick sort pivot middle reverse error. result="+print(result), check(result)); + result = QuickSort.sort(QuickSort.PIVOT_TYPE.RANDOM, unsorted.clone()); + assertTrue("Quick sort pivot random unsorted error. result="+print(result), check(result)); + result = QuickSort.sort(QuickSort.PIVOT_TYPE.RANDOM, sorted.clone()); + assertTrue("Quick sort pivot random sorted error. result="+print(result), check(result)); + result = QuickSort.sort(QuickSort.PIVOT_TYPE.RANDOM, reverse.clone()); + assertTrue("Quick sort pivot random reverse error. result="+print(result), check(result)); + } + + @Test + public void testHeapSorts() { + // Heapsort + Integer[] result = HeapSort.sort(unsorted.clone()); + assertTrue("Heap sort unsorted error. result="+print(result), check(result)); + result = HeapSort.sort(sorted.clone()); + assertTrue("Heap sort sorted error. result="+print(result), check(result)); + result = HeapSort.sort(reverse.clone()); + assertTrue("Heap sort reverse error. result="+print(result), check(result)); + } + + @Test + public void testCountingSorts() { + // Counting sort + Integer[] result = CountingSort.sort(unsorted.clone()); + assertTrue("Counting sort unsorted error. result="+print(result), check(result)); + result = CountingSort.sort(sorted.clone()); + assertTrue("Counting sort sorted error. result="+print(result), check(result)); + result = CountingSort.sort(reverse.clone()); + assertTrue("Counting sort reverse error. result="+print(result), check(result)); + } + + @Test + public void testRadixSorts() { + // Radix sort + Integer[] result = RadixSort.sort(unsorted.clone()); + assertTrue("Radix sort unsorted error. result="+print(result), check(result)); + result = RadixSort.sort(sorted.clone()); + assertTrue("Radix sort sorted error. result="+print(result), check(result)); + result = RadixSort.sort(reverse.clone()); + assertTrue("Radix sort reverse error. result="+print(result), check(result)); + } + + @Test + public void testAmericanFlagSorts() { + // American Flag sort + Integer[] result = AmericanFlagSort.sort(unsorted.clone()); + assertTrue("American flag sort unsorted error. result="+print(result), check(result)); + result = AmericanFlagSort.sort(sorted.clone()); + assertTrue("American flag sort sorted error. result="+print(result), check(result)); + result = AmericanFlagSort.sort(reverse.clone()); + assertTrue("American flag sort reverse error. result="+print(result), check(result)); + } + + private static final boolean check(Integer[] array) { + for (int i = 1; iarray[i]) + return false; + } + return true; + } + + private static final String print(Integer[] array) { + return print(array, 0, array.length); + } + + private static final String print(Integer[] array, int start, int length) { + final Integer[] clone = array.clone(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i= 0; i--) { + reverse[i] = (SIZE - 1) - i; + } + System.out.println("Generated reverse sorted array."); + System.out.println(); + System.out.flush(); + + System.out.println("Starting sorts..."); + System.out.println(); + System.out.flush(); + + results[count] = new double[1 * 3]; + count = runTest(new Insertion(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new Bubble(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new Shell(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new MergeInPlace(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new MergeNotInPlace(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new QuickFirst(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new QuickMiddle(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new QuickRandom(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new Heap(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new Counting(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new Radix(), unsorted, sorted, names, results[count], count); + showComparison(); + + results[count] = new double[1 * 3]; + count = runTest(new AmericanFlag(), unsorted, sorted, names, results[count], count); + showComparison(); + } + + private static final int runTest(Testable testable, Integer[] unsorted, Integer[] sorted, String[] names, double[] results, int count) { + names[count] = testable.getName(); + + long bInsertion = System.nanoTime(); + Integer[] result = testable.sort(unsorted.clone()); + if (checkResults && !check(result)) + System.err.println(testable.getName()+" failed."); + long aInsertion = System.nanoTime(); + double diff = (aInsertion - bInsertion) / 1000000d / 1000d; + System.out.println("Random: "+testable.getName()+"=" + FORMAT.format(diff) + " secs"); + if (showResultingArray) + showResultingArray(unsorted, result); + results[0] = diff; + putOutTheGarbage(); + + bInsertion = System.nanoTime(); + result = testable.sort(sorted.clone()); + if (checkResults && !check(result)) + System.err.println(testable.getName()+" failed."); + aInsertion = System.nanoTime(); + diff = (aInsertion - bInsertion) / 1000000d / 1000d; + System.out.println("Sorted: "+testable.getName()+"=" + FORMAT.format(diff) + " secs"); + if (showResultingArray) + showResultingArray(sorted, result); + results[1] = diff; + putOutTheGarbage(); + + bInsertion = System.nanoTime(); + result = testable.sort(reverse.clone()); + if (checkResults && !check(result)) + System.err.println(testable.getName()+" failed."); + aInsertion = System.nanoTime(); + diff = (aInsertion - bInsertion) / 1000000d / 1000d; + System.out.println("Reverse sorted: "+testable.getName()+"=" + FORMAT.format(diff) + " secs"); + if (showResultingArray) + showResultingArray(reverse, result); + results[2] = diff; + putOutTheGarbage(); + + System.out.println(); + System.out.flush(); + + return count+1; + } + + public static abstract class Testable { + String input = null; + public String getInput() { + return input; + } + public abstract String getName(); + public abstract Integer[] sort(Integer[] input); + } + + private static class AmericanFlag extends Testable { + @Override + public String getName() { + return "AmericanFlag sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return AmericanFlagSort.sort(input); + } + } + + private static class Bubble extends Testable { + @Override + public String getName() { + return "Bubble sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return BubbleSort.sort(input); + } + } + + private static class Counting extends Testable { + @Override + public String getName() { + return "Counting sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return CountingSort.sort(input); + } + } + + private static class Heap extends Testable { + @Override + public String getName() { + return "Heap sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return HeapSort.sort(input); + } + } + + private static class Insertion extends Testable { + @Override + public String getName() { + return "Insertion sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return InsertionSort.sort(input); + } + } + + private static class MergeInPlace extends Testable { + @Override + public String getName() { + return "MergeInPlace sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return MergeSort.sort(MergeSort.SPACE_TYPE.IN_PLACE, input); + } + } + + private static class MergeNotInPlace extends Testable { + @Override + public String getName() { + return "MergeInPlace sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return MergeSort.sort(MergeSort.SPACE_TYPE.NOT_IN_PLACE, input); + } + } + + private static class QuickFirst extends Testable { + @Override + public String getName() { + return "Quick (first) sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return QuickSort.sort(QuickSort.PIVOT_TYPE.FIRST, input); + } + } + + private static class QuickMiddle extends Testable { + @Override + public String getName() { + return "Quick (middle) sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return QuickSort.sort(QuickSort.PIVOT_TYPE.MIDDLE, input); + } + } + + private static class QuickRandom extends Testable { + @Override + public String getName() { + return "Quick (random) sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return QuickSort.sort(QuickSort.PIVOT_TYPE.RANDOM, input); + } + } + + private static class Radix extends Testable { + @Override + public String getName() { + return "Radix sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return RadixSort.sort(input); + } + } + + private static class Shell extends Testable { + int[] shells = new int[] { 10, 5, 3, 1 }; + + @Override + public String getName() { + return "Shell sort"; + } + + @Override + public Integer[] sort(Integer[] input) { + return ShellSort.sort(shells, input); + } + } + + private static final void showComparison() { + StringBuilder resultsBuilder = new StringBuilder(); + resultsBuilder.append("Number of integers = ").append(SIZE).append("\n"); + String format = "%-32s%-15s%-15s%-15s\n"; + Formatter formatter = new Formatter(resultsBuilder, Locale.US); + + formatter.format(format, "Algorithm","Random","Sorted","Reverse Sorted"); + for (int i=0; i array[i]) + return false; + } + return true; + } + + public static final String print(Integer[] array) { + return print(array, 0, array.length); + } + + public static final String print(Integer[] array, int start, int length) { + final Integer[] clone = array.clone(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < length; i++) { + int e = clone[start + i]; + builder.append(e + " "); + } + return builder.toString(); + } + + public static final String printWithPivot(Integer[] array, int pivotIndex, int start, int length) { + final Integer[] clone = array.clone(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < length; i++) { + int e = clone[start + i]; + if (i == pivotIndex) + builder.append("`" + e + "` "); + else + builder.append(e + " "); + } + return builder.toString(); + } + + private static final void putOutTheGarbage() { + collectGarbage(); + collectGarbage(); + collectGarbage(); + } + + private static final long fSLEEP_INTERVAL = 100; + + private static final void collectGarbage() { + try { + System.gc(); + System.gc(); + System.gc(); + Thread.sleep(fSLEEP_INTERVAL); + System.runFinalization(); + Thread.sleep(fSLEEP_INTERVAL); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } +} diff --git a/test/com/jwetherell/algorithms/strings/test/KnuthMorrisPrattTests.java b/test/com/jwetherell/algorithms/strings/test/KnuthMorrisPrattTests.java new file mode 100644 index 00000000..bc3f00de --- /dev/null +++ b/test/com/jwetherell/algorithms/strings/test/KnuthMorrisPrattTests.java @@ -0,0 +1,37 @@ +package com.jwetherell.algorithms.strings.test; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +import com.jwetherell.algorithms.strings.KnuthMorrisPratt; + +public class KnuthMorrisPrattTests { + + @Test + @SuppressWarnings("unchecked") + public void getPrefixSuffixes() throws Exception { + final List data = Arrays.asList( + new Object[][]{ + {"", Arrays.asList()}, + {"a", Arrays.asList(0)}, + {"aaa", Arrays.asList(0, 1, 2)}, + {"abbabb", Arrays.asList(0, 0, 0, 1, 2, 3)}, + {"bbabbbbaab", Arrays.asList(0, 1, 0, 1, 2, 2, 2, 3, 0, 1)}, + { + "( ͡° ͜ʖ ͡° )( ͡° a( ͡° ͜ʖ ͡°",//"( ͡° ͜ʖ ͡° )( ͡° a( ͡° ͜ʖ ͡°", + Arrays.asList(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + } + } + ); + for (Object[] testCase: data) { + String input = (String) testCase[0]; + List expected = (List) testCase[1]; + List result = KnuthMorrisPratt.getPrefSufTable(input); + assertEquals(expected, result); + } + } +} \ No newline at end of file diff --git a/test/com/jwetherell/algorithms/strings/test/ManacherTests.java b/test/com/jwetherell/algorithms/strings/test/ManacherTests.java new file mode 100644 index 00000000..276575ff --- /dev/null +++ b/test/com/jwetherell/algorithms/strings/test/ManacherTests.java @@ -0,0 +1,39 @@ +package com.jwetherell.algorithms.strings.test; + +import static org.junit.Assert.assertEquals; +import java.util.List; +import java.util.Arrays; +import com.jwetherell.algorithms.strings.Manacher; +import org.junit.Test; + +public class ManacherTests { + + @Test + public void testGetLongestPalindromicSubstring() throws Exception { + final List data = Arrays.asList( + new Object[][]{ + {null, null}, + {"", ""}, + {"a", "a"}, + {"aa", "aa"}, + {"aaa", "aaa"}, + {"abaa", "aba"}, + {"abba", "abba"}, + {"abbaaa", "abba"}, + {"abbabb", "bbabb"}, + {"bananas", "anana"}, + {"bakskskba", "ksksk"}, + {"itisneveroddoreven", "neveroddoreven"}, + {"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "A"}, + {"I like bananas", "anana"} + + } + ); + for (Object[] testCase: data) { + String input = (String) testCase[0]; + String expected = (String) testCase[1]; + String result = Manacher.getLongestPalindromicSubstring(input); + assertEquals(expected, result); + } + } +} diff --git a/test/com/jwetherell/algorithms/strings/test/StringRotation.java b/test/com/jwetherell/algorithms/strings/test/StringRotation.java new file mode 100644 index 00000000..3b1be244 --- /dev/null +++ b/test/com/jwetherell/algorithms/strings/test/StringRotation.java @@ -0,0 +1,80 @@ +package com.jwetherell.algorithms.strings.test; + +import com.jwetherell.algorithms.strings.Rotation; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class StringRotation { + + private String reverse(String text) { + return new StringBuilder(text).reverse().toString(); + } + + @Test + public void minimalRotationTest() { + assertEquals("", + Rotation.getLexicographicallyMinimalRotation("")); + + assertEquals("a", + Rotation.getLexicographicallyMinimalRotation("a")); + + assertEquals("abcdefgh", + Rotation.getLexicographicallyMinimalRotation("abcdefgh")); + + assertEquals("abcdefgh", + Rotation.getLexicographicallyMinimalRotation("bcdefgha")); + + assertEquals("abbbbbbb", + Rotation.getLexicographicallyMinimalRotation("bbbbbabb")); + + + StringBuilder builder = new StringBuilder(); + for(char c = 'Z'; c > 'A'; c--) { + for(int i = 0; i<4000; i++) + builder.append(c); + for(char c2 = c; c2 <= 'Z'; c2++) + builder.append(c2); + } + String str = builder.toString(); + + assertEquals('A'+str+str, + Rotation.getLexicographicallyMinimalRotation(str + 'A' + str)); + + assertEquals('A'+str+reverse(str)+str, + Rotation.getLexicographicallyMinimalRotation(reverse(str) + str + 'A' + str)); + } + + @Test + public void maximalRotationTest() { + assertEquals("", + Rotation.getLexicographicallyMaximalRotation("")); + + assertEquals("a", + Rotation.getLexicographicallyMaximalRotation("a")); + + assertEquals("habcdefg", + Rotation.getLexicographicallyMaximalRotation("abcdefgh")); + + assertEquals("habcdefg", + Rotation.getLexicographicallyMaximalRotation("habcdefg")); + + assertEquals("cbbbbbbb", + Rotation.getLexicographicallyMaximalRotation("bbbbbcbb")); + + StringBuilder builder = new StringBuilder(); + for(char c = 'A'; c < 'Z'; c++) { + for(int i = 0; i<4000; i++) + builder.append(c); + for(char c2 = c; c2 >='A'; c2--) + builder.append(c2); + } + String str = builder.toString(); + + assertEquals('Z'+str+str, + Rotation.getLexicographicallyMaximalRotation(str + 'Z' + str)); + + assertEquals('Z'+str+reverse(str)+str, + Rotation.getLexicographicallyMaximalRotation(reverse(str) + str + 'Z' + str)); + } +} diff --git a/test/com/jwetherell/algorithms/strings/test/Strings.java b/test/com/jwetherell/algorithms/strings/test/Strings.java new file mode 100644 index 00000000..466581e1 --- /dev/null +++ b/test/com/jwetherell/algorithms/strings/test/Strings.java @@ -0,0 +1,106 @@ +package com.jwetherell.algorithms.strings.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Test; + +import com.jwetherell.algorithms.strings.StringFunctions; + +public class Strings { + + @Test + public void testReverseCharsInString() { + // REVERSE CHARS IN A STRING + String string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + String check = "zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA"; + String result = StringFunctions.reverseWithStringConcat(string); + assertTrue("Reverse With String Concat error. expect="+check+" got="+result, check.equals(result)); + + result = StringFunctions.reverseWithStringBuilder(string); + assertTrue("Reverse With String String Builder error. expect="+check+" got="+result, check.equals(result)); + + result = StringFunctions.reverseWithStringBuilderBuiltinMethod(string); + assertTrue("Reverse With Built-in error. expect="+check+" got="+result, check.equals(result)); + + result = StringFunctions.reverseWithSwaps(string); + assertTrue("Reverse With Swaps error. expect="+check+" got="+result, check.equals(result)); + + result = StringFunctions.reverseWithXOR(string); + assertTrue("Reverse With XOR error. expect="+check+" got="+result, check.equals(result)); + } + + @Test + public void testReverseWordsInString() { + // REVERSE WORDS IN A STRING + String string = "Could you pretty please reverse this sentence"; + String check = "sentence this reverse please pretty you Could"; + String result = StringFunctions.reverseWordsByCharWithAdditionalStorage(string); + assertTrue("Reverse Words By Char w/ Additional Storage error. expect="+check+" got="+result, check.equals(result)); + + result = StringFunctions.reverseWordsUsingStringTokenizerWithAdditionalStorage(string); + assertTrue("Reverse Words Using String Tokenizer w/ Additional Storage error. expect="+check+" got="+result, check.equals(result)); + + result = StringFunctions.reverseWordsUsingSplitWithAdditionalStorage(string); + assertTrue("Reverse Words Using Split w/ Additional Storage error. expect="+check+" got="+result, check.equals(result)); + + result = StringFunctions.reverseWordsInPlace(string); + assertTrue("Reverse Words In-Place error. expect="+check+" got="+result, check.equals(result)); + } + + @Test + public void testIsPalinDrone() { + // PALINDROME + String string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + boolean result = StringFunctions.isPalindromeWithAdditionalStorage(string); + assertFalse("Is Palindrome With Additional Storage error. expected=false got="+result, result); + + result = StringFunctions.isPalindromeInPlace(string); + assertFalse("Is Palindrome In Place error. expected=false got="+result, result); + + string = "ABCDEFGHIJKKJIHGFEDCBA"; + result = StringFunctions.isPalindromeWithAdditionalStorage(string); + assertTrue("Is Palindrome With Additional Storage error. expected=true got="+result, result); + + result = StringFunctions.isPalindromeInPlace(string); + assertTrue("Is Palindrome In Place error. expected=true got="+result, result); + } + + @Test + public void testGenerateSubSets() { + // COMBINATIONS + String string = "abc"; + String[] check = new String[] {"", "c", "b", "cb", "a", "ca", "ba", "cba"}; + String[] result = StringFunctions.generateSubsets(string); + assertTrue("Generate Subsets error. expected="+print(check)+" got="+print(result), Arrays.equals(check, result)); + } + + @Test + public void testEditDistanceRecursive() { + // Edit Distance + String string1 = "kitten"; + String string2 = "sitting"; + int check = 3; + int result = StringFunctions.levenshteinDistanceRecursive(string1, string2); + assertTrue("Edit Distance error. expected="+check+" got="+result, (check==result)); + } + + @Test + public void testEditDistanceDP() { + // Edit Distance + String string1 = "kitten"; + String string2 = "sitting"; + int check = 3; + int result = StringFunctions.levenshteinDistanceIterative(string1, string2); + assertTrue("Edit Distance error. expected="+check+" got="+result, (check==result)); + } + + private static final String print(String[] strings) { + StringBuilder builder = new StringBuilder(); + for (String s : strings) + builder.append(s).append(' '); + return builder.toString(); + } +} diff --git a/src/com/jwetherell/algorithms/Strings.java b/test/com/jwetherell/algorithms/strings/timing/StringsTiming.java similarity index 67% rename from src/com/jwetherell/algorithms/Strings.java rename to test/com/jwetherell/algorithms/strings/timing/StringsTiming.java index ccb607a3..7e6fa7c5 100644 --- a/src/com/jwetherell/algorithms/Strings.java +++ b/test/com/jwetherell/algorithms/strings/timing/StringsTiming.java @@ -1,10 +1,10 @@ -package com.jwetherell.algorithms; +package com.jwetherell.algorithms.strings.timing; import java.text.DecimalFormat; import com.jwetherell.algorithms.strings.StringFunctions; -public class Strings { +public class StringsTiming { private static final DecimalFormat FORMAT = new DecimalFormat("#.######"); @@ -14,41 +14,40 @@ public static void main(String[] args) { String string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; System.out.println("Reversing a string using concatination."); long before = System.nanoTime(); - String result = StringFunctions.reverseWithStringConcat(string); + StringFunctions.reverseWithStringConcat(string); long after = System.nanoTime(); - System.out.println("before=" + string + " after=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Reversing a string with a StringBuilder."); before = System.nanoTime(); - result = StringFunctions.reverseWithStringBuilder(string); + StringFunctions.reverseWithStringBuilder(string); after = System.nanoTime(); - System.out.println("before=" + string + " after=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Reversing a string with StringBuilder built-in reverse method."); before = System.nanoTime(); - result = StringFunctions.reverseWithStringBuilderBuiltinMethod(string); + StringFunctions.reverseWithStringBuilderBuiltinMethod(string); after = System.nanoTime(); - System.out.println("before=" + string + " after=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Reversing a string with swaps."); before = System.nanoTime(); - result = StringFunctions.reverseWithSwaps(string); + StringFunctions.reverseWithSwaps(string); after = System.nanoTime(); - System.out.println("before=" + string + " after=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Reversing a string with XOR."); before = System.nanoTime(); - result = StringFunctions.reverseWithXOR(string); + StringFunctions.reverseWithXOR(string); after = System.nanoTime(); - System.out.println("before=" + string + " after=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); @@ -59,33 +58,32 @@ public static void main(String[] args) { String string = "Could you pretty please reverse this sentence"; System.out.println("Reversing a string using additional array."); long before = System.nanoTime(); - String result = StringFunctions.reverseWordsByCharWithAdditionalStorage(string); + StringFunctions.reverseWordsByCharWithAdditionalStorage(string); long after = System.nanoTime(); - System.out.println("before=" + string + " after=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Reversing a string using StringTokenizer with additional storage."); before = System.nanoTime(); - result = StringFunctions.reverseWordsUsingStringTokenizerWithAdditionalStorage(string); + StringFunctions.reverseWordsUsingStringTokenizerWithAdditionalStorage(string); after = System.nanoTime(); - System.out.println("before=" + string + " after=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Reversing a string using split with additional storage."); before = System.nanoTime(); - result = StringFunctions.reverseWordsUsingStringTokenizerWithAdditionalStorage(string); + StringFunctions.reverseWordsUsingStringTokenizerWithAdditionalStorage(string); after = System.nanoTime(); - System.out.println("before=" + string + " after=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); System.out.println("Reversing a string in-place."); before = System.nanoTime(); - result = StringFunctions.reverseWordsInPlace(string); + StringFunctions.reverseWordsInPlace(string); after = System.nanoTime(); - System.out.println("before=" + string + " after=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); @@ -96,32 +94,33 @@ public static void main(String[] args) { String string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; System.out.println("Is Palindrome with additional storage?"); long before = System.nanoTime(); - boolean result = StringFunctions.isPalindromeWithAdditionalStorage(string); + StringFunctions.isPalindromeWithAdditionalStorage(string); long after = System.nanoTime(); - System.out.println("string=" + string + " isPalindrome=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); + System.out.println("Is Palindrome in-place?"); before = System.nanoTime(); - result = StringFunctions.isPalindromeInPlace(string); + StringFunctions.isPalindromeInPlace(string); after = System.nanoTime(); - System.out.println("string=" + string + " isPalindrome=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); string = "ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUTSRQPONMLKJIHGFEDCBA"; System.out.println("Is Palindrome with additional storage?"); before = System.nanoTime(); - result = StringFunctions.isPalindromeWithAdditionalStorage(string); + StringFunctions.isPalindromeWithAdditionalStorage(string); after = System.nanoTime(); - System.out.println("string=" + string + " isPalindrome=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); System.gc(); + System.out.println("Is Palindrome in-place?"); before = System.nanoTime(); - result = StringFunctions.isPalindromeInPlace(string); + StringFunctions.isPalindromeInPlace(string); after = System.nanoTime(); - System.out.println("string=" + string + " isPalindrome=" + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); @@ -132,9 +131,21 @@ public static void main(String[] args) { String string = "abc"; System.out.println("All possible subsets."); long before = System.nanoTime(); - String[] result = StringFunctions.generateSubsets(string); + StringFunctions.generateSubsets(string); + long after = System.nanoTime(); + System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); + System.out.println(); + System.gc(); + } + + // Edit Distance + { + String string1 = "kitten"; + String string2 = "sitting"; + System.out.println("Edit Distance Recursive"); + long before = System.nanoTime(); + StringFunctions.levenshteinDistanceRecursive(string1, string2); long after = System.nanoTime(); - System.out.println("string=" + string + " subsets=" + result.length); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc(); @@ -144,12 +155,10 @@ public static void main(String[] args) { { String string1 = "kitten"; String string2 = "sitting"; - System.out.println("Edit Distance"); + System.out.println("Edit Distance Iterative"); long before = System.nanoTime(); - int result = StringFunctions.levenshteinDistance(string1, string2); + StringFunctions.levenshteinDistanceIterative(string1, string2); long after = System.nanoTime(); - System.out.println("string1='" + string1 + "' string2='" + string2 + "' edit (levenshtein) distance=" - + result); System.out.println("Computed in " + FORMAT.format(after - before) + " ns"); System.out.println(); System.gc();