import java.util.Scanner; import java.util.Random; import java.util.ArrayList; import java.io.File; import java.io.FileNotFoundException; /* Part3ColorTester.java ** Author: R. McCloskey ** Date: March 2020 (improvement over February version in that ** choosing pseudo-random red, white, and blue ** elements to place into the array should be ** more efficient, at leat for large arrays). ** ** Java application that tests instances of the PartitionerUpperLower ** and PartitionerDigitLetter classes. Instances of these classes can ** perform 3-color partitioning on arrays of type char[]. ** ** An instance of one of these two classes, as chosen by the user, is ** created, and its partition(), partition2(), and partition3() methods ** are applied to identical arrays. The char values in the arrays are ** pseudo-randomly generated (using an instance of java.util.Random), with ** the user choosing how many elements of each color are to be present. ** To avoid having to deal with "unprintable" char values, only characters ** with ASCII codes 32..126 (space..tilde) are candidates to be included ** in the array. ** ** In order to facilitate testing, the user is prompted to enter a seed ** to be used in creating the Random object; the contents of the array ** are determined by the seed value together with the user's choices of ** how many characters of each color are to be placed in the array. */ public class Part3ColorTester { private static final int RED_CODE = 0; private static final int WHITE_CODE = 1; private static final int BLUE_CODE = 2; private static final char SPACE = ' '; private static final char TILDE = '~'; private static final char NULL_CHAR = (char)0; private static Scanner keyboard; private static boolean echo; public static void main(String[] args) throws FileNotFoundException { // If a command line argument is provided, interpret it to be the // name of a file containing input data. if (args.length > 0) { File file = new File(args[0]); keyboard = new Scanner(file); echo = true; } else { // Otherwise, input is read from the keyboard. keyboard = new Scanner(System.in); echo = false; } System.out.println("Welcome to the partitioner tester program!"); String classPrompt = "Enter 1 to test PartitionerUpperLower\n" + "Enter 2 to test PartitionerDigitLette\n" + "> "; Partitioner3Color part = null; int whichClass = getInt(classPrompt,1,2); if (whichClass == 1) { part = new PartitionerUpperLower(); } else { part = new PartitionerDigitLetter(); } // Construct ArrayLists that contain all red, white, and blue // characters, respectively, according to how part classifies them. ArrayList redChars = listOfChars(part, RED_CODE); ArrayList whiteChars = listOfChars(part, WHITE_CODE); ArrayList blueChars = listOfChars(part, BLUE_CODE); String promptSuffix = " to place into the array: "; String redPrompt = "Enter # of RED elements"; String whitePrompt = "Enter # of WHITE elements"; String bluePrompt = "Enter # of BLUE elements"; String randPrompt = "Enter (int) seed for pseudo-random # generation: "; int numRed = getInt(redPrompt + promptSuffix, 0, 1000); int numWhite = getInt(whitePrompt + promptSuffix, 0, 1000); int numBlue = getInt(bluePrompt + promptSuffix, 0, 1000); int randSeed = getInt(randPrompt,0,32767); Random rand = new Random(randSeed); char[] a = randomArray(part, rand, redChars, numRed, whiteChars, numWhite, blueChars, numBlue); performTest(part, a.clone(), NULL_CHAR); performTest(part, a.clone(), '2'); performTest(part, a, '3'); System.out.println("\nGoodbye."); } private static void performTest(Partitioner3Color p, char[] ary, char methodCode) { System.out.printf("\nAbout to call partition%c() on this array:\n", methodCode); displayArraySegment(ary, 0, ary.length); System.out.println(); try { if (methodCode == NULL_CHAR) { p.partition(ary); } else if (methodCode == '2') { p.partition2(ary); } else if (methodCode == '3') { p.partition3(ary); } else { throw new IllegalArgumentException(); } System.out.printf("\nAfter calling partition%c(), we have:\n", methodCode); reportResults(p); } catch (Throwable e) { e.printStackTrace(System.out); } } /* Displays the results of most recent partitioning. */ private static void reportResults(Partitioner3Color part) { char[] ary = part.array(); System.out.print("Partitioned array: "); displayArraySegment(ary, 0, ary.length); System.out.println(); int rwBoundary = part.whiteStart(); int wbBoundary = part.blueStart(); System.out.printf("RED segment [%d,%d): ", 0, rwBoundary); displayArraySegment(ary, 0, rwBoundary); System.out.println(); System.out.printf("WHITE segment [%d,%d): ", rwBoundary, wbBoundary); displayArraySegment(ary, rwBoundary, wbBoundary); System.out.println(); System.out.printf("BLUE segment [%d,%d): ", wbBoundary, ary.length); displayArraySegment(ary, wbBoundary, ary.length); System.out.println(); System.out.println("# color tests: " + part.numColorTests()); System.out.println("# swaps: " + part.numSwaps()); } /* Prints the specified string and returns the response entered at the ** keyboard, interpreted as an integer. */ private static int getInt(String prompt, int min, int max) { int result; boolean finished = false; do { System.out.print(prompt); result = keyboard.nextInt(); if (echo) { System.out.println(result); } if (min <= result && result <= max) { finished = true; } else { System.out.printf("Error: response must be in range %d..%d\n", min, max); } } while (!finished); return result; } /* Prints the values in the specified array segment (i.e., z[low..high)). */ private static void displayArraySegment(char[] z, int low, int high) { for (int i = low; i != high; i++) { System.out.print(z[i]); } } /* Returns an ArrayList containing every character (in the range ' '..'~') ** that is of the color specified by the 2nd parameter, as classified by ** the 1st parameter. */ private static ArrayList listOfChars(Partitioner3Color p, int colorCode) { ArrayList result = new ArrayList(); for (char ch = SPACE; ch <= TILDE; ch = (char)(ch + 1)) { if ((colorCode == RED_CODE && p.isRed(ch)) || (colorCode == WHITE_CODE && p.isWhite(ch)) || (colorCode == BLUE_CODE && p.isBlue(ch)) ) { result.add(ch); } } return result; } /* Returns a new array of type char[] containing the specified number ** of RED, WHITE, and BLUE values, generated by and put into pseudo-random ** order using the given Random object. */ private static char[] randomArray(Partitioner3Color p, Random r, ArrayList redElems, int redCount, ArrayList whiteElems, int whiteCount, ArrayList blueElems, int blueCount) { int N = redCount + whiteCount + blueCount; char[] result = new char[N]; fillWith(r, redElems, result, 0, redCount); fillWith(r, whiteElems, result, redCount, redCount+whiteCount); fillWith(r, blueElems, result, redCount+whiteCount, N); permute(result, r); return result; } /* Places pseudo-randomly chosen values from 'items' into the elements ** of the specified array segment (a[low..high)). */ private static void fillWith(Random rand, ArrayList items, char[] a, int low, int high) { final int M = items.size(); for (int i = low; i < high; i++) { a[i] = items.get(rand.nextInt(M)); } } /* Permutes the elements of the given array pseudo-randomly, using ** the given Random object. */ private static void permute(char[] a, Random rand) { for (int i = a.length; i > 1; i--) { // Swap the value at location i-1 with that at some location // in the range [0..i). int randLocation = rand.nextInt(i); swap(a, i-1, randLocation); } } /* Swaps the elements at the specified locations of the specified array. */ private static void swap(char[] a, int j, int k) { char temp = a[j]; a[j] = a[k]; a[k] = temp; } }