/* This Java application is for the purpose of testing a class that purports ** to provide information about the longest common subsequences of two given ** Strings. ** ** Author: R. McCloskey ** Date: April 2019 */ public class LongComSubseq_Tester { /* Solves the instance of the LCS problem described by two Strings ** provided via command line arguments. */ public static void main(String[] args) { // Name the two given strings x and y String x = args[0]; String y = args[1]; // Create an object that computes the LCS of x and y LongComSubseq_504_S19 lcs = new LongComSubseq_504_S19(x, y); // Print the table indicating LLCS values for all prefixes of x and y. System.out.println("LLCS table for " + x + " and " + y + ": \n\n"); lcs.printLLCSTable(); System.out.println(); // Get the length of any LCS of x and y, and report it. int maxLen = lcs.lengthOfLCS(); System.out.println("Length of any LCS is " + maxLen); // Get one LCS of x and y and report it String lcsStr = lcs.longestCommonSubsequence(); System.out.println("A longest common subsequence is " + lcsStr); // If the reported length of any LCS differs from the length of // the LCS that was reported, alert the user. if (lcsStr.length() != maxLen) { System.out.println("** Error: Length " + lcsStr.length() + " is inconsistent with LLCS table, which says " + maxLen + " **"); } // If the reported LCS is not a common subsequence of x and y, // alert the user. if (!isCommonSubsequence(lcsStr, x, y)) { System.out.println("** Error: Not a common subsequence!! **"); } // Get a matching between x and y and report it. int[][] matching = lcs.longestMatching(); System.out.println("A longest matching is:"); printMatching(matching); // If it is not a valid matching, alert the user. if (!isMatching(matching, x, y)) { System.out.println("** Error: That is NOT a valid matching! **"); } if (matching[0].length != maxLen) { System.out.println("** Error: Length of matching is not consistent " + "with length of LCS! **"); } } /* Reports whether the given string s is a subsequence of both x and y. */ public static boolean isCommonSubsequence(String s, String x, String y) { return isSubsequenceOf(s,x) && isSubsequenceOf(s,y); } /* Reports whether or not s is a subsequence of v. */ private static boolean isSubsequenceOf(String s, String v) { final int s_LEN = s.length(); final int v_LEN = v.length(); int i=0, j=0; // loop invariant: s[0..i) is the longest prefix of s that is // a subsequence of v[0..j). while (i != s_LEN && j != v_LEN && (s_LEN - i <= v_LEN - j)) { if (s.charAt(i) == v.charAt(j)) { i = i+1; } j = j+1; } return i == s_LEN; } /* Reports whether the given array of arrays is a valid matching between ** given Strings u and v. To be valid, it must have length two and its ** elements (both of which are of type int[]) must have the same lengths ** and their values must be strictly increasing and in the range [0,m) ** and [0..n), respectively. Furthermore, elements in corresponding ** positions of matching[0] and matching[1] must "point to" characters ** in u and v that are equal. In other words, the parameter 'matching' ** must meet these conditions: ** (1) matching.length == 2 ** (2) matching[0].length == matching[1].length ** (3) for i = 0,1 and all relevant k, matching[i][k-1] < matching[i][k] ** (4a) for all relevant k, 0<= matching[0] < u.length() ** (4b) for all relevant k, 0<= matching[1] < v.length() ** (5) For all k, u.charAt(matching[0][k]) == v.charAt(matching[1][k]). */ public static boolean isMatching(int[][] matching, String u, String v) { if (matching.length != 2) { return false; } else { int[] f = matching[0]; int[] g = matching[1]; if (f.length != g.length) { return false; } else if (!isIncreasing(f)) { return false; } else if (!isIncreasing(g])) { return false; } else if (!inRange(f, 0, u.length())) { return false; } else if (!inRange(g, 0, v.length())) { return false; } else { final int LEN = matching[0].length; int i=0; //loop invariant 0<=i<=LEN & // u[f[j]] = v[g[j]] for all j in [0..i) while (i != LEN && u.charAt(f[i]) == v.charAt(g[i])) { i++; } return i == LEN; } } } /* Reports whether the given array's elements are in increasing order ** (meaning that each one is greater than its predecessor). */ private static boolean isIncreasing(int[] a) { final int LEN = a.length; if (LEN < 2) { return true; } else { int k = 1; // loop invariant: for all i in range [1..k), a[i-1] < a[i] while (k != LEN && a[k-1] < a[k]) { k++; } return k == LEN; } } /* Reports whether all elements in the given array have a value in the ** specified range (i.e., [low..high)). */ private static boolean inRange(int[] a, int low, int high) { int k = 0; // loop invariant: all elements in a[0..k) are in the range [low..high). while (k != a.length && low <= a[k] && a[k] < high) { k++; } return k == a.length; } /* Prints the elements in a matching. */ private static void printMatching(int[][] mat) { final int LEN = mat[0].length; for (int i=0; i != 2; i++) { for (int j=0; j != LEN; j++) { System.out.printf("%3d ", mat[i][j]); } System.out.println(); } } }