diff --git a/common/In.java b/common/In.java new file mode 100644 index 0000000..7540767 --- /dev/null +++ b/common/In.java @@ -0,0 +1,801 @@ +package common; +/****************************************************************************** + * Compilation: javac In.java + * Execution: java In (basic test --- see source for required files) + * Dependencies: none + * + * Reads in data of various types from standard input, files, and URLs. + * + ******************************************************************************/ + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.Socket; +import java.net.URL; +import java.net.URLConnection; + +import java.util.ArrayList; +import java.util.InputMismatchException; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.Scanner; +import java.util.regex.Pattern; + +/** + * The In data type provides methods for reading strings + * and numbers from standard input, file input, URLs, and sockets. + *

+ * The Locale used is: language = English, country = US. This is consistent + * with the formatting conventions with Java floating-point literals, + * command-line arguments (via {@link Double#parseDouble(String)}) + * and standard output. + *

+ * For additional documentation, see + * Section 3.1 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + *

+ * Like {@link Scanner}, reading a token also consumes preceding Java + * whitespace, reading a full line consumes + * the following end-of-line delimiter, while reading a character consumes + * nothing extra. + *

+ * Whitespace is defined in {@link Character#isWhitespace(char)}. Newlines + * consist of \n, \r, \r\n, and Unicode hex code points 0x2028, 0x2029, 0x0085; + * see + * Scanner.java (NB: Java 6u23 and earlier uses only \r, \r, \r\n). + * + * @author David Pritchard + * @author Robert Sedgewick + * @author Kevin Wayne + */ +public final class In { + + ///// begin: section (1 of 2) of code duplicated from In to StdIn. + + // assume Unicode UTF-8 encoding + private static final String CHARSET_NAME = "UTF-8"; + + // assume language = English, country = US for consistency with System.out. + private static final Locale LOCALE = Locale.US; + + // the default token separator; we maintain the invariant that this value + // is held by the scanner's delimiter between calls + private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\p{javaWhitespace}+"); + + // makes whitespace characters significant + private static final Pattern EMPTY_PATTERN = Pattern.compile(""); + + // used to read the entire input. source: + // http://weblogs.java.net/blog/pat/archive/2004/10/stupid_scanner_1.html + private static final Pattern EVERYTHING_PATTERN = Pattern.compile("\\A"); + + //// end: section (1 of 2) of code duplicated from In to StdIn. + + private Scanner scanner; + + /** + * Initializes an input stream from standard input. + */ + public In() { + scanner = new Scanner(new BufferedInputStream(System.in), CHARSET_NAME); + scanner.useLocale(LOCALE); + } + + /** + * Initializes an input stream from a socket. + * + * @param socket the socket + * @throws IllegalArgumentException if cannot open {@code socket} + * @throws IllegalArgumentException if {@code socket} is {@code null} + */ + public In(Socket socket) { + if (socket == null) throw new IllegalArgumentException("socket argument is null"); + try { + InputStream is = socket.getInputStream(); + scanner = new Scanner(new BufferedInputStream(is), CHARSET_NAME); + scanner.useLocale(LOCALE); + } + catch (IOException ioe) { + throw new IllegalArgumentException("could not open socket: " + socket, ioe); + } + } + + /** + * Initializes an input stream from a URL. + * + * @param url the URL + * @throws IllegalArgumentException if cannot open {@code url} + * @throws IllegalArgumentException if {@code url} is {@code null} + */ + public In(URL url) { + if (url == null) throw new IllegalArgumentException("url argument is null"); + try { + URLConnection site = url.openConnection(); + InputStream is = site.getInputStream(); + scanner = new Scanner(new BufferedInputStream(is), CHARSET_NAME); + scanner.useLocale(LOCALE); + } + catch (IOException ioe) { + throw new IllegalArgumentException("could not read URL: '" + url + "'", ioe); + } + } + + /** + * Initializes an input stream from a file. + * + * @param file the file + * @throws IllegalArgumentException if cannot open {@code file} + * @throws IllegalArgumentException if {@code file} is {@code null} + */ + public In(File file) { + if (file == null) throw new IllegalArgumentException("file argument is null"); + try { + // for consistency with StdIn, wrap with BufferedInputStream instead of use + // file as argument to Scanner + FileInputStream fis = new FileInputStream(file); + scanner = new Scanner(new BufferedInputStream(fis), CHARSET_NAME); + scanner.useLocale(LOCALE); + } + catch (IOException ioe) {; + throw new IllegalArgumentException("could not read file: " + file, ioe); + } + } + + + /** + * Initializes an input stream from a filename or web page name. + * + * @param name the filename or web page name + * @throws IllegalArgumentException if cannot open {@code name} as + * a file or URL + * @throws IllegalArgumentException if {@code name} is {@code null} + */ + public In(String name) { + if (name == null) throw new IllegalArgumentException("argument is null"); + if (name.length() == 0) throw new IllegalArgumentException("argument is the empty string"); + try { + // first try to read file from local file system + File file = new File(name); + if (file.exists()) { + // for consistency with StdIn, wrap with BufferedInputStream instead of use + // file as argument to Scanner + FileInputStream fis = new FileInputStream(file); + scanner = new Scanner(new BufferedInputStream(fis), CHARSET_NAME); + scanner.useLocale(LOCALE); + return; + } + + // resource relative to .class file + URL url = getClass().getResource(name); + + // resource relative to classloader root + if (url == null) { + url = getClass().getClassLoader().getResource(name); + } + + // or URL from web + if (url == null) { + URI uri = new URI(name); + if (uri.isAbsolute()) url = uri.toURL(); + else throw new IllegalArgumentException("could not read: '" + name + "'"); + url = new URL(name); + } + + URLConnection site = url.openConnection(); + + // in order to set User-Agent, replace above line with these two + // HttpURLConnection site = (HttpURLConnection) url.openConnection(); + // site.addRequestProperty("User-Agent", "Mozilla/4.76"); + + InputStream is = site.getInputStream(); + scanner = new Scanner(new BufferedInputStream(is), CHARSET_NAME); + scanner.useLocale(LOCALE); + } + catch (IOException | URISyntaxException e) { + throw new IllegalArgumentException("could not read: '" + name + "'"); + } + } + + /** + * Initializes an input stream from a given {@link Scanner} source; use with + * {@code new Scanner(String)} to read from a string. + *

+ * Note that this does not create a defensive copy, so the + * scanner will be mutated as you read on. + * + * @param scanner the scanner + * @throws IllegalArgumentException if {@code scanner} is {@code null} + */ + public In(Scanner scanner) { + if (scanner == null) throw new IllegalArgumentException("scanner argument is null"); + this.scanner = scanner; + } + + /** + * Returns true if this input stream exists. + * + * @return {@code true} if this input stream exists; {@code false} otherwise + */ + public boolean exists() { + return scanner != null; + } + + //// begin: section (2 of 2) of code duplicated from In to StdIn, + //// with all methods changed from "public" to "public static". + + /** + * Returns true if input stream is empty (except possibly whitespace). + * Use this to know whether the next call to {@link #readString()}, + * {@link #readDouble()}, etc. will succeed. + * + * @return {@code true} if this input stream is empty (except possibly whitespace); + * {@code false} otherwise + */ + public boolean isEmpty() { + return !scanner.hasNext(); + } + + /** + * Returns true if this input stream has a next line. + * Use this method to know whether the + * next call to {@link #readLine()} will succeed. + * This method is functionally equivalent to {@link #hasNextChar()}. + * + * @return {@code true} if this input stream has more input (including whitespace); + * {@code false} otherwise + */ + public boolean hasNextLine() { + return scanner.hasNextLine(); + } + + /** + * Returns true if this input stream has more input (including whitespace). + * Use this method to know whether the next call to {@link #readChar()} will succeed. + * This method is functionally equivalent to {@link #hasNextLine()}. + * + * @return {@code true} if this input stream has more input (including whitespace); + * {@code false} otherwise + */ + public boolean hasNextChar() { + scanner.useDelimiter(EMPTY_PATTERN); + boolean result = scanner.hasNext(); + scanner.useDelimiter(WHITESPACE_PATTERN); + return result; + } + + + /** + * Reads and returns the next line in this input stream. + * + * @return the next line in this input stream; {@code null} if no such line + */ + public String readLine() { + String line; + try { + line = scanner.nextLine(); + } + catch (NoSuchElementException e) { + line = null; + } + return line; + } + + /** + * Reads and returns the next character in this input stream. + * + * @return the next {@code char} in this input stream + * @throws NoSuchElementException if the input stream is empty + */ + public char readChar() { + scanner.useDelimiter(EMPTY_PATTERN); + try { + String ch = scanner.next(); + assert ch.length() == 1 : "Internal (Std)In.readChar() error!" + + " Please contact the authors."; + scanner.useDelimiter(WHITESPACE_PATTERN); + return ch.charAt(0); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'char' value from the input stream, " + + "but no more tokens are available"); + } + } + + + /** + * Reads and returns the remainder of this input stream, as a string. + * + * @return the remainder of this input stream, as a string + */ + public String readAll() { + if (!scanner.hasNextLine()) + return ""; + + String result = scanner.useDelimiter(EVERYTHING_PATTERN).next(); + // not that important to reset delimeter, since now scanner is empty + scanner.useDelimiter(WHITESPACE_PATTERN); // but let's do it anyway + return result; + } + + + /** + * Reads the next token from this input stream and returns it as a {@code String}. + * + * @return the next {@code String} in this input stream + * @throws NoSuchElementException if the input stream is empty + */ + public String readString() { + try { + return scanner.next(); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'String' value from the input stream, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from this input stream, parses it as a {@code int}, + * and returns the {@code int}. + * + * @return the next {@code int} in this input stream + * @throws NoSuchElementException if the input stream is empty + * @throws InputMismatchException if the next token cannot be parsed as an {@code int} + */ + public int readInt() { + try { + return scanner.nextInt(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read an 'int' value from the input stream, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attemps to read an 'int' value from the input stream, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from this input stream, parses it as a {@code double}, + * and returns the {@code double}. + * + * @return the next {@code double} in this input stream + * @throws NoSuchElementException if the input stream is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code double} + */ + public double readDouble() { + try { + return scanner.nextDouble(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'double' value from the input stream, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attemps to read a 'double' value from the input stream, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from this input stream, parses it as a {@code float}, + * and returns the {@code float}. + * + * @return the next {@code float} in this input stream + * @throws NoSuchElementException if the input stream is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code float} + */ + public float readFloat() { + try { + return scanner.nextFloat(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'float' value from the input stream, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attemps to read a 'float' value from the input stream, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from this input stream, parses it as a {@code long}, + * and returns the {@code long}. + * + * @return the next {@code long} in this input stream + * @throws NoSuchElementException if the input stream is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code long} + */ + public long readLong() { + try { + return scanner.nextLong(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'long' value from the input stream, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attemps to read a 'long' value from the input stream, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from this input stream, parses it as a {@code short}, + * and returns the {@code short}. + * + * @return the next {@code short} in this input stream + * @throws NoSuchElementException if the input stream is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code short} + */ + public short readShort() { + try { + return scanner.nextShort(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'short' value from the input stream, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attemps to read a 'short' value from the input stream, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from this input stream, parses it as a {@code byte}, + * and returns the {@code byte}. + *

+ * To read binary data, use {@link BinaryIn}. + * + * @return the next {@code byte} in this input stream + * @throws NoSuchElementException if the input stream is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code byte} + */ + public byte readByte() { + try { + return scanner.nextByte(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'byte' value from the input stream, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attemps to read a 'byte' value from the input stream, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from this input stream, parses it as a {@code boolean} + * (interpreting either {@code "true"} or {@code "1"} as {@code true}, + * and either {@code "false"} or {@code "0"} as {@code false}). + * + * @return the next {@code boolean} in this input stream + * @throws NoSuchElementException if the input stream is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code boolean} + */ + public boolean readBoolean() { + try { + String token = readString(); + if ("true".equalsIgnoreCase(token)) return true; + if ("false".equalsIgnoreCase(token)) return false; + if ("1".equals(token)) return true; + if ("0".equals(token)) return false; + throw new InputMismatchException("attempts to read a 'boolean' value from the input stream, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'boolean' value from the input stream, " + + "but no more tokens are available"); + } + } + + /** + * Reads all remaining tokens from this input stream and returns them as + * an array of strings. + * + * @return all remaining tokens in this input stream, as an array of strings + */ + public String[] readAllStrings() { + // we could use readAll.trim().split(), but that's not consistent + // since trim() uses characters 0x00..0x20 as whitespace + String[] tokens = WHITESPACE_PATTERN.split(readAll()); + if (tokens.length == 0 || tokens[0].length() > 0) + return tokens; + String[] decapitokens = new String[tokens.length-1]; + for (int i = 0; i < tokens.length-1; i++) + decapitokens[i] = tokens[i+1]; + return decapitokens; + } + + /** + * Reads all remaining lines from this input stream and returns them as + * an array of strings. + * + * @return all remaining lines in this input stream, as an array of strings + */ + public String[] readAllLines() { + ArrayList lines = new ArrayList(); + while (hasNextLine()) { + lines.add(readLine()); + } + return lines.toArray(new String[0]); + } + + + /** + * Reads all remaining tokens from this input stream, parses them as integers, + * and returns them as an array of integers. + * + * @return all remaining lines in this input stream, as an array of integers + */ + public int[] readAllInts() { + String[] fields = readAllStrings(); + int[] vals = new int[fields.length]; + for (int i = 0; i < fields.length; i++) + vals[i] = Integer.parseInt(fields[i]); + return vals; + } + + /** + * Reads all remaining tokens from this input stream, parses them as longs, + * and returns them as an array of longs. + * + * @return all remaining lines in this input stream, as an array of longs + */ + public long[] readAllLongs() { + String[] fields = readAllStrings(); + long[] vals = new long[fields.length]; + for (int i = 0; i < fields.length; i++) + vals[i] = Long.parseLong(fields[i]); + return vals; + } + + /** + * Reads all remaining tokens from this input stream, parses them as doubles, + * and returns them as an array of doubles. + * + * @return all remaining lines in this input stream, as an array of doubles + */ + public double[] readAllDoubles() { + String[] fields = readAllStrings(); + double[] vals = new double[fields.length]; + for (int i = 0; i < fields.length; i++) + vals[i] = Double.parseDouble(fields[i]); + return vals; + } + + ///// end: section (2 of 2) of code duplicated from In to StdIn */ + + /** + * Closes this input stream. + */ + public void close() { + scanner.close(); + } + + /** + * Reads all integers from a file and returns them as + * an array of integers. + * + * @param filename the name of the file + * @return the integers in the file + * @deprecated Replaced by {@code new In(filename)}.{@link #readAllInts()}. + */ + @Deprecated + public static int[] readInts(String filename) { + return new In(filename).readAllInts(); + } + + /** + * Reads all doubles from a file and returns them as + * an array of doubles. + * + * @param filename the name of the file + * @return the doubles in the file + * @deprecated Replaced by {@code new In(filename)}.{@link #readAllDoubles()}. + */ + @Deprecated + public static double[] readDoubles(String filename) { + return new In(filename).readAllDoubles(); + } + + /** + * Reads all strings from a file and returns them as + * an array of strings. + * + * @param filename the name of the file + * @return the strings in the file + * @deprecated Replaced by {@code new In(filename)}.{@link #readAllStrings()}. + */ + @Deprecated + public static String[] readStrings(String filename) { + return new In(filename).readAllStrings(); + } + + /** + * Reads all integers from standard input and returns them + * an array of integers. + * + * @return the integers on standard input + * @deprecated Replaced by {@code new In()}.{@link #readAllInts()}. + */ + @Deprecated + public static int[] readInts() { + return new In().readAllInts(); + } + + /** + * Reads all doubles from standard input and returns them as + * an array of doubles. + * + * @return the doubles on standard input + * @deprecated Replaced by {@code new In()}.{@link #readAllDoubles()}. + */ + @Deprecated + public static double[] readDoubles() { + return new In().readAllDoubles(); + } + + /** + * Reads all strings from standard input and returns them as + * an array of strings. + * + * @return the strings on standard input + * @deprecated Replaced by {@code new In()}.{@link #readAllStrings()}. + */ + @Deprecated + public static String[] readStrings() { + return new In().readAllStrings(); + } + + /** + * Unit tests the {@code In} data type. + * + * @param args the command-line arguments + */ + public static void main(String[] args) { + In in; + String urlName = "https://introcs.cs.princeton.edu/java/stdlib/InTest.txt"; + + // read from a URL + System.out.println("readAll() from URL " + urlName); + System.out.println("---------------------------------------------------------------------------"); + try { + in = new In(urlName); + System.out.println(in.readAll()); + } + catch (IllegalArgumentException e) { + System.out.println(e); + } + System.out.println(); + + // read one line at a time from URL + System.out.println("readLine() from URL " + urlName); + System.out.println("---------------------------------------------------------------------------"); + try { + in = new In(urlName); + while (!in.isEmpty()) { + String s = in.readLine(); + System.out.println(s); + } + } + catch (IllegalArgumentException e) { + System.out.println(e); + } + System.out.println(); + + // read one string at a time from URL + System.out.println("readString() from URL " + urlName); + System.out.println("---------------------------------------------------------------------------"); + try { + in = new In(urlName); + while (!in.isEmpty()) { + String s = in.readString(); + System.out.println(s); + } + } + catch (IllegalArgumentException e) { + System.out.println(e); + } + System.out.println(); + + + // read one line at a time from file in current directory + System.out.println("readLine() from current directory"); + System.out.println("---------------------------------------------------------------------------"); + try { + in = new In("./InTest.txt"); + while (!in.isEmpty()) { + String s = in.readLine(); + System.out.println(s); + } + } + catch (IllegalArgumentException e) { + System.out.println(e); + } + System.out.println(); + + + // read one line at a time from file using relative path + System.out.println("readLine() from relative path"); + System.out.println("---------------------------------------------------------------------------"); + try { + in = new In("../stdlib/InTest.txt"); + while (!in.isEmpty()) { + String s = in.readLine(); + System.out.println(s); + } + } + catch (IllegalArgumentException e) { + System.out.println(e); + } + System.out.println(); + + // read one char at a time + System.out.println("readChar() from file"); + System.out.println("---------------------------------------------------------------------------"); + try { + in = new In("InTest.txt"); + while (!in.isEmpty()) { + char c = in.readChar(); + System.out.print(c); + } + } + catch (IllegalArgumentException e) { + System.out.println(e); + } + System.out.println(); + System.out.println(); + + // read one line at a time from absolute OS X / Linux path + System.out.println("readLine() from absolute OS X / Linux path"); + System.out.println("---------------------------------------------------------------------------"); + try { + in = new In("/n/fs/introcs/www/java/stdlib/InTest.txt"); + while (!in.isEmpty()) { + String s = in.readLine(); + System.out.println(s); + } + } + catch (IllegalArgumentException e) { + System.out.println(e); + } + System.out.println(); + + + // read one line at a time from absolute Windows path + System.out.println("readLine() from absolute Windows path"); + System.out.println("---------------------------------------------------------------------------"); + try { + in = new In("G:\\www\\introcs\\stdlib\\InTest.txt"); + while (!in.isEmpty()) { + String s = in.readLine(); + System.out.println(s); + } + System.out.println(); + } + catch (IllegalArgumentException e) { + System.out.println(e); + } + System.out.println(); + + } + +} diff --git a/common/Out.java b/common/Out.java new file mode 100644 index 0000000..6673544 --- /dev/null +++ b/common/Out.java @@ -0,0 +1,331 @@ +package common; +/****************************************************************************** + * Compilation: javac Out.java + * Execution: java Out + * Dependencies: none + * + * Writes data of various types to: stdout, file, or socket. + * + ******************************************************************************/ + + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.Socket; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Locale; + +/** + * The Out data type provides methods for writing strings and + * numbers to various output streams, including standard output, file, and sockets. + *

+ * For additional documentation, see + * Section 3.1 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + */ +public class Out { + + // force Unicode UTF-8 encoding; otherwise it's system dependent + private static final Charset CHARSET = StandardCharsets.UTF_8; + + // assume language = English, country = US for consistency with In + private static final Locale LOCALE = Locale.US; + + private PrintWriter out; + + /** + * Initializes an output stream from a {@link OutputStream}. + * + * @param os the {@code OutputStream} + */ + public Out(OutputStream os) { + OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET); + out = new PrintWriter(osw, true); + } + + /** + * Initializes an output stream from standard output. + */ + public Out() { + this(System.out); + } + + /** + * Initializes an output stream from a socket. + * + * @param socket the socket + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if cannot create output stream from socket + */ + public Out(Socket socket) { + if (socket == null) { + throw new IllegalArgumentException("socket argument is null"); + } + try { + OutputStream os = socket.getOutputStream(); + OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET); + out = new PrintWriter(osw, true); + } + catch (IOException e) { + throw new IllegalArgumentException("could not create output stream from socket", e); + } + } + + /** + * Initializes an output stream from a file. + * + * @param filename the name of the file + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} is the empty string + * @throws IllegalArgumentException if cannot write the file {@code filename} + */ + public Out(String filename) { + if (filename == null) { + throw new IllegalArgumentException("filename argument is null"); + } + + if (filename.length() == 0) { + throw new IllegalArgumentException("filename argument is the empty string"); + } + + try { + OutputStream os = new FileOutputStream(filename); + OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET); + out = new PrintWriter(osw, true); + } + catch (IOException e) { + throw new IllegalArgumentException("could not create file '" + filename + "' for writing", e); + } + } + + /** + * Closes the output stream. + */ + public void close() { + out.close(); + } + + /** + * Terminates the current line by printing the line-separator string. + */ + public void println() { + out.println(); + } + + /** + * Prints an object to this output stream and then terminates the line. + * + * @param x the object to print + */ + public void println(Object x) { + out.println(x); + } + + /** + * Prints a boolean to this output stream and then terminates the line. + * + * @param x the boolean to print + */ + public void println(boolean x) { + out.println(x); + } + + /** + * Prints a character to this output stream and then terminates the line. + * + * @param x the character to print + */ + public void println(char x) { + out.println(x); + } + + /** + * Prints a double to this output stream and then terminates the line. + * + * @param x the double to print + */ + public void println(double x) { + out.println(x); + } + + /** + * Prints a float to this output stream and then terminates the line. + * + * @param x the float to print + */ + public void println(float x) { + out.println(x); + } + + /** + * Prints an integer to this output stream and then terminates the line. + * + * @param x the integer to print + */ + public void println(int x) { + out.println(x); + } + + /** + * Prints a long to this output stream and then terminates the line. + * + * @param x the long to print + */ + public void println(long x) { + out.println(x); + } + + /** + * Prints a byte to this output stream and then terminates the line. + *

+ * To write binary data, see {@link BinaryOut}. + * + * @param x the byte to print + */ + public void println(byte x) { + out.println(x); + } + + + + /** + * Flushes this output stream. + */ + public void print() { + out.flush(); + } + + /** + * Prints an object to this output stream and flushes this output stream. + * + * @param x the object to print + */ + public void print(Object x) { + out.print(x); + out.flush(); + } + + /** + * Prints a boolean to this output stream and flushes this output stream. + * + * @param x the boolean to print + */ + public void print(boolean x) { + out.print(x); + out.flush(); + } + + /** + * Prints a character to this output stream and flushes this output stream. + * + * @param x the character to print + */ + public void print(char x) { + out.print(x); + out.flush(); + } + + /** + * Prints a double to this output stream and flushes this output stream. + * + * @param x the double to print + */ + public void print(double x) { + out.print(x); + out.flush(); + } + + /** + * Prints a float to this output stream and flushes this output stream. + * + * @param x the float to print + */ + public void print(float x) { + out.print(x); + out.flush(); + } + + /** + * Prints an integer to this output stream and flushes this output stream. + * + * @param x the integer to print + */ + public void print(int x) { + out.print(x); + out.flush(); + } + + /** + * Prints a long integer to this output stream and flushes this output stream. + * + * @param x the long integer to print + */ + public void print(long x) { + out.print(x); + out.flush(); + } + + /** + * Prints a byte to this output stream and flushes this output stream. + * + * @param x the byte to print + */ + public void print(byte x) { + out.print(x); + out.flush(); + } + + /** + * Prints a formatted string to this output stream, using the specified format + * string and arguments, and then flushes this output stream. + * + * @param format the format string + * @param args the arguments accompanying the format string + */ + public void printf(String format, Object... args) { + out.printf(LOCALE, format, args); + out.flush(); + } + + /** + * Prints a formatted string to this output stream, using the specified + * locale, format string, and arguments, and then flushes this output stream. + * + * @param locale the locale + * @param format the format string + * @param args the arguments accompanying the format string + */ + public void printf(Locale locale, String format, Object... args) { + out.printf(locale, format, args); + out.flush(); + } + + + /** + * A test client. + * + * @param args the command-line arguments + */ + public static void main(String[] args) { + Out out; + + // write to stdout + out = new Out(); + out.println("Test 1"); + out.close(); + + // write to a file + out = new Out("test.txt"); + out.println("Test 2"); + out.close(); + } + +} diff --git a/common/README.md b/common/README.md new file mode 100644 index 0000000..8d656d6 --- /dev/null +++ b/common/README.md @@ -0,0 +1,11 @@ +# Common libraries + +## Typst + +Simple utility to render classes + +## Java + +Sourced from https://magnus-madsen.github.io/course-intprog/book.html. + +Files under [GPL](https://www.gnu.org/licenses/gpl-3.0.html) \ No newline at end of file diff --git a/common/StdArrayIO.java b/common/StdArrayIO.java new file mode 100644 index 0000000..4e54983 --- /dev/null +++ b/common/StdArrayIO.java @@ -0,0 +1,281 @@ +package common; +/****************************************************************************** + * Compilation: javac StdArrayIO.java + * Execution: java StdArrayIO < input.txt + * Dependencies: StdOut.java + * Data files: https://introcs.cs.princeton.edu/java/22library/tinyDouble1D.txt + * https://introcs.cs.princeton.edu/java/22library/tinyDouble2D.txt + * https://introcs.cs.princeton.edu/java/22library/tinyBoolean2D.txt + * + * A library for reading in 1D and 2D arrays of integers, doubles, + * and booleans from standard input and printing them out to + * standard output. + * + * % more tinyDouble1D.txt + * 4 + * .000 .246 .222 -.032 + * + * % more tinyDouble2D.txt + * 4 3 + * .000 .270 .000 + * .246 .224 -.036 + * .222 .176 .0893 + * -.032 .739 .270 + * + * % more tinyBoolean2D.txt + * 4 3 + * 1 1 0 + * 0 0 0 + * 0 1 1 + * 1 1 1 + * + * % cat tinyDouble1D.txt tinyDouble2D.txt tinyBoolean2D.txt | java StdArrayIO + * 4 + * 0.00000 0.24600 0.22200 -0.03200 + * + * 4 3 + * 0.00000 0.27000 0.00000 + * 0.24600 0.22400 -0.03600 + * 0.22200 0.17600 0.08930 + * 0.03200 0.73900 0.27000 + * + * 4 3 + * 1 1 0 + * 0 0 0 + * 0 1 1 + * 1 1 1 + * + ******************************************************************************/ + + +/** + * The StdArrayIO class provides static methods for reading + * in 1D and 2D arrays from standard input and printing out to + * standard output. + *

+ * For additional documentation, see + * Section 2.2 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + */ +public class StdArrayIO { + + // it doesn't make sense to instantiate this class + private StdArrayIO() { } + + /** + * Reads a 1D array of doubles from standard input and returns it. + * + * @return the 1D array of doubles + */ + public static double[] readDouble1D() { + int n = StdIn.readInt(); + double[] a = new double[n]; + for (int i = 0; i < n; i++) { + a[i] = StdIn.readDouble(); + } + return a; + } + + /** + * Prints an array of doubles to standard output. + * + * @param a the 1D array of doubles + */ + public static void print(double[] a) { + int n = a.length; + StdOut.println(n); + for (int i = 0; i < n; i++) { + StdOut.printf("%9.5f ", a[i]); + } + StdOut.println(); + } + + /** + * Reads a 2D array of doubles from standard input and returns it. + * + * @return the 2D array of doubles + */ + public static double[][] readDouble2D() { + int m = StdIn.readInt(); + int n = StdIn.readInt(); + double[][] a = new double[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + a[i][j] = StdIn.readDouble(); + } + } + return a; + } + + /** + * Prints the 2D array of doubles to standard output. + * + * @param a the 2D array of doubles + */ + public static void print(double[][] a) { + int m = a.length; + int n = a[0].length; + StdOut.println(m + " " + n); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + StdOut.printf("%9.5f ", a[i][j]); + } + StdOut.println(); + } + } + + + /** + * Reads a 1D array of integers from standard input and returns it. + * + * @return the 1D array of integers + */ + public static int[] readInt1D() { + int n = StdIn.readInt(); + int[] a = new int[n]; + for (int i = 0; i < n; i++) { + a[i] = StdIn.readInt(); + } + return a; + } + + /** + * Prints an array of integers to standard output. + * + * @param a the 1D array of integers + */ + public static void print(int[] a) { + int n = a.length; + StdOut.println(n); + for (int i = 0; i < n; i++) { + StdOut.printf("%9d ", a[i]); + } + StdOut.println(); + } + + /** + * Reads a 2D array of integers from standard input and returns it. + * + * @return the 2D array of integers + */ + public static int[][] readInt2D() { + int m = StdIn.readInt(); + int n = StdIn.readInt(); + int[][] a = new int[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + a[i][j] = StdIn.readInt(); + } + } + return a; + } + + /** + * Print a 2D array of integers to standard output. + * + * @param a the 2D array of integers + */ + public static void print(int[][] a) { + int m = a.length; + int n = a[0].length; + StdOut.println(m + " " + n); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + StdOut.printf("%9d ", a[i][j]); + } + StdOut.println(); + } + } + + /** + * Reads a 1D array of booleans from standard input and returns it. + * + * @return the 1D array of booleans + */ + public static boolean[] readBoolean1D() { + int n = StdIn.readInt(); + boolean[] a = new boolean[n]; + for (int i = 0; i < n; i++) { + a[i] = StdIn.readBoolean(); + } + return a; + } + + /** + * Prints a 1D array of booleans to standard output. + * + * @param a the 1D array of booleans + */ + public static void print(boolean[] a) { + int n = a.length; + StdOut.println(n); + for (int i = 0; i < n; i++) { + if (a[i]) StdOut.print("1 "); + else StdOut.print("0 "); + } + StdOut.println(); + } + + /** + * Reads a 2D array of booleans from standard input and returns it. + * + * @return the 2D array of booleans + */ + public static boolean[][] readBoolean2D() { + int m = StdIn.readInt(); + int n = StdIn.readInt(); + boolean[][] a = new boolean[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + a[i][j] = StdIn.readBoolean(); + } + } + return a; + } + + /** + * Prints a 2D array of booleans to standard output. + * + * @param a the 2D array of booleans + */ + public static void print(boolean[][] a) { + int m = a.length; + int n = a[0].length; + StdOut.println(m + " " + n); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (a[i][j]) StdOut.print("1 "); + else StdOut.print("0 "); + } + StdOut.println(); + } + } + + /** + * Unit tests {@code StdArrayIO}. + * + * @param args the command-line arguments + */ + public static void main(String[] args) { + + // read and print an array of doubles + double[] a = StdArrayIO.readDouble1D(); + StdArrayIO.print(a); + StdOut.println(); + + // read and print a matrix of doubles + double[][] b = StdArrayIO.readDouble2D(); + StdArrayIO.print(b); + StdOut.println(); + + // read and print a matrix of doubles + boolean[][] d = StdArrayIO.readBoolean2D(); + StdArrayIO.print(d); + StdOut.println(); + } + +} diff --git a/common/StdIn.java b/common/StdIn.java new file mode 100644 index 0000000..80c134f --- /dev/null +++ b/common/StdIn.java @@ -0,0 +1,669 @@ +package common; +/****************************************************************************** + * Compilation: javac StdIn.java + * Execution: java StdIn (interactive test of basic functionality) + * Dependencies: none + * + * Reads in data of various types from standard input. + * + ******************************************************************************/ + +import java.util.ArrayList; +import java.util.InputMismatchException; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.Scanner; +import java.util.regex.Pattern; + +/** + * The {@code StdIn} class provides static methods for reading strings + * and numbers from standard input. + * These functions fall into one of four categories: + *

+ *

+ * Generally, it is best not to mix functions from the different + * categories in the same program. + *

+ * Getting started. + * To use this class, you must have {@code StdIn.class} in your + * Java classpath. If you used our autoinstaller, you should be all set. + * Otherwise, either download + * stdlib.jar + * and add to your Java classpath or download + * StdIn.java + * and put a copy in your working directory. + *

+ * Reading tokens from standard input and converting to numbers and strings. + * You can use the following methods to read numbers, strings, and booleans + * from standard input one at a time: + *

+ *

+ * The first method returns true if standard input has no more tokens. + * Each other method skips over any input that is whitespace. Then, it reads + * the next token and attempts to convert it into a value of the specified + * type. If it succeeds, it returns that value; otherwise, it + * throws an {@link InputMismatchException}. + *

+ * Whitespace includes spaces, tabs, and newlines; the full definition + * is inherited from {@link Character#isWhitespace(char)}. + * A token is a maximal sequence of non-whitespace characters. + * The precise rules for describing which tokens can be converted to + * integers and floating-point numbers are inherited from + * Scanner, + * using the locale {@link Locale#US}; the rules + * for floating-point numbers are slightly different + * from those in {@link Double#valueOf(String)}, + * but unlikely to be of concern to most programmers. + *

+ * As an example, the following code fragment reads integers from standard input, + * one at a time, and prints them one per line. + *

+ *  while (!StdIn.isEmpty()) {
+ *      double value = StdIn.readDouble();
+ *      StdOut.println(value);
+ *  }
+ *  
+ *

+ * Reading characters from standard input. + * You can use the following two methods to read characters from standard input one at a time: + *

+ *

+ * The first method returns true if standard input has more input (including whitespace). + * The second method reads and returns the next character of input on standard + * input (possibly a whitespace character). + *

+ * As an example, the following code fragment reads characters from standard input, + * one character at a time, and prints it to standard output. + *

+ *  while (StdIn.hasNextChar()) {
+ *      char c = StdIn.readChar();
+ *      StdOut.print(c);
+ *  }
+ *  
+ *

+ * Reading lines from standard input. + * You can use the following two methods to read lines from standard input: + *

+ *

+ * The first method returns true if standard input has more input (including whitespace). + * The second method reads and returns the remaining portion of + * the next line of input on standard input (possibly whitespace), + * discarding the trailing line separator. + *

+ * A line separator is defined to be one of the following strings: + * {@code \n} (Linux), {@code \r} (old Macintosh), + * {@code \r\n} (Windows), + * {@code \}{@code u2028}, {@code \}{@code u2029}, or {@code \}{@code u0085}. + *

+ * As an example, the following code fragment reads text from standard input, + * one line at a time, and prints it to standard output. + *

+ *  while (StdIn.hasNextLine()) {
+ *      String line = StdIn.readLine();
+ *      StdOut.println(line);
+ *  }
+ *  
+ *

+ * Reading a sequence of values of the same type from standard input. + * You can use the following methods to read a sequence numbers, strings, + * or booleans (all of the same type) from standard input: + *

+ *

+ * The first three methods read of all of remaining token on standard input + * and converts the tokens to values of + * the specified type, as in the corresponding + * {@code readDouble}, {@code readInt}, and {@code readString()} methods. + * The {@code readAllLines()} method reads all remaining lines on standard + * input and returns them as an array of strings. + * The {@code readAll()} method reads all remaining input on standard + * input and returns it as a string. + *

+ * As an example, the following code fragment reads all of the remaining + * tokens from standard input and returns them as an array of strings. + *

+ *  String[] words = StdIn.readAllStrings();
+ *  
+ *

+ * Differences with Scanner. + * {@code StdIn} and {@link Scanner} are both designed to parse + * tokens and convert them to primitive types and strings. + * The main differences are summarized below: + *

+ *

+ * Historical note: {@code StdIn} preceded {@code Scanner}; when + * {@code Scanner} was introduced, this class was re-implemented to use {@code Scanner}. + *

+ * Using standard input. + * Standard input is a fundamental operating system abstraction on Mac OS X, + * Windows, and Linux. + * The methods in {@code StdIn} are blocking, which means that they + * will wait until you enter input on standard input. + * If your program has a loop that repeats until standard input is empty, + * you must signal that the input is finished. + * To do so, depending on your operating system and IDE, + * use either {@code } or {@code }, on its own line. + * If you are redirecting standard input from a file, you will not need + * to do anything to signal that the input is finished. + *

+ * Known bugs. + * Java's UTF-8 encoding does not recognize the optional + * byte-order mask. + * If the input begins with the optional byte-order mask, {@code StdIn} + * will have an extra character {@code \}{@code uFEFF} at the beginning. + *

+ * Reference. + * For additional documentation, + * see Section 1.5 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + * @author David Pritchard + */ +public final class StdIn { + + /*** begin: section (1 of 2) of code duplicated from In to StdIn. */ + + // assume Unicode UTF-8 encoding + private static final String CHARSET_NAME = "UTF-8"; + + // assume language = English, country = US for consistency with System.out. + private static final Locale LOCALE = Locale.US; + + // the default token separator; we maintain the invariant that this value + // is held by the scanner's delimiter between calls + private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\p{javaWhitespace}+"); + + // makes whitespace significant + private static final Pattern EMPTY_PATTERN = Pattern.compile(""); + + // used to read the entire input + private static final Pattern EVERYTHING_PATTERN = Pattern.compile("\\A"); + + /*** end: section (1 of 2) of code duplicated from In to StdIn. */ + + private static Scanner scanner; + + // it doesn't make sense to instantiate this class + private StdIn() { } + + //// begin: section (2 of 2) of code duplicated from In to StdIn, + //// with all methods changed from "public" to "public static" + + /** + * Returns true if standard input is empty (except possibly for whitespace). + * Use this method to know whether the next call to {@link #readString()}, + * {@link #readDouble()}, etc. will succeed. + * + * @return {@code true} if standard input is empty (except possibly + * for whitespace); {@code false} otherwise + */ + public static boolean isEmpty() { + return !scanner.hasNext(); + } + + /** + * Returns true if standard input has a next line. + * Use this method to know whether the + * next call to {@link #readLine()} will succeed. + * This method is functionally equivalent to {@link #hasNextChar()}. + * + * @return {@code true} if standard input has more input (including whitespace); + * {@code false} otherwise + */ + public static boolean hasNextLine() { + return scanner.hasNextLine(); + } + + /** + * Returns true if standard input has more input (including whitespace). + * Use this method to know whether the next call to {@link #readChar()} will succeed. + * This method is functionally equivalent to {@link #hasNextLine()}. + * + * @return {@code true} if standard input has more input (including whitespace); + * {@code false} otherwise + */ + public static boolean hasNextChar() { + scanner.useDelimiter(EMPTY_PATTERN); + boolean result = scanner.hasNext(); + scanner.useDelimiter(WHITESPACE_PATTERN); + return result; + } + + + /** + * Reads and returns the next line, excluding the line separator if present. + * + * @return the next line, excluding the line separator if present; + * {@code null} if no such line + */ + public static String readLine() { + String line; + try { + line = scanner.nextLine(); + } + catch (NoSuchElementException e) { + line = null; + } + return line; + } + + /** + * Reads and returns the next character. + * + * @return the next {@code char} + * @throws NoSuchElementException if standard input is empty + */ + public static char readChar() { + try { + scanner.useDelimiter(EMPTY_PATTERN); + String ch = scanner.next(); + assert ch.length() == 1 : "Internal (Std)In.readChar() error!" + + " Please contact the authors."; + scanner.useDelimiter(WHITESPACE_PATTERN); + return ch.charAt(0); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'char' value from standard input, " + + "but no more tokens are available"); + } + } + + /** + * Reads and returns the remainder of the input, as a string. + * + * @return the remainder of the input, as a string + * @throws NoSuchElementException if standard input is empty + */ + public static String readAll() { + if (!scanner.hasNextLine()) + return ""; + + String result = scanner.useDelimiter(EVERYTHING_PATTERN).next(); + // not that important to reset delimiter, since now scanner is empty + scanner.useDelimiter(WHITESPACE_PATTERN); // but let's do it anyway + return result; + } + + + /** + * Reads the next token from standard input and returns it as a {@code String}. + * + * @return the next {@code String} + * @throws NoSuchElementException if standard input is empty + */ + public static String readString() { + try { + return scanner.next(); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'String' value from standard input, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from standard input, parses it as an integer, and returns the integer. + * + * @return the next integer on standard input + * @throws NoSuchElementException if standard input is empty + * @throws InputMismatchException if the next token cannot be parsed as an {@code int} + */ + public static int readInt() { + try { + return scanner.nextInt(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read an 'int' value from standard input, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attemps to read an 'int' value from standard input, " + + "but no more tokens are available"); + } + + } + + /** + * Reads the next token from standard input, parses it as a double, and returns the double. + * + * @return the next double on standard input + * @throws NoSuchElementException if standard input is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code double} + */ + public static double readDouble() { + try { + return scanner.nextDouble(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'double' value from standard input, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'double' value from standard input, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from standard input, parses it as a float, and returns the float. + * + * @return the next float on standard input + * @throws NoSuchElementException if standard input is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code float} + */ + public static float readFloat() { + try { + return scanner.nextFloat(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'float' value from standard input, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'float' value from standard input, " + + "but there no more tokens are available"); + } + } + + /** + * Reads the next token from standard input, parses it as a long integer, and returns the long integer. + * + * @return the next long integer on standard input + * @throws NoSuchElementException if standard input is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code long} + */ + public static long readLong() { + try { + return scanner.nextLong(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'long' value from standard input, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'long' value from standard input, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from standard input, parses it as a short integer, and returns the short integer. + * + * @return the next short integer on standard input + * @throws NoSuchElementException if standard input is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code short} + */ + public static short readShort() { + try { + return scanner.nextShort(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'short' value from standard input, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'short' value from standard input, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from standard input, parses it as a byte, and returns the byte. + * + * @return the next byte on standard input + * @throws NoSuchElementException if standard input is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code byte} + */ + public static byte readByte() { + try { + return scanner.nextByte(); + } + catch (InputMismatchException e) { + String token = scanner.next(); + throw new InputMismatchException("attempts to read a 'byte' value from standard input, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'byte' value from standard input, " + + "but no more tokens are available"); + } + } + + /** + * Reads the next token from standard input, parses it as a boolean, + * and returns the boolean. + * + * @return the next boolean on standard input + * @throws NoSuchElementException if standard input is empty + * @throws InputMismatchException if the next token cannot be parsed as a {@code boolean}: + * {@code true} or {@code 1} for true, and {@code false} or {@code 0} for false, + * ignoring case + */ + public static boolean readBoolean() { + try { + String token = readString(); + if ("true".equalsIgnoreCase(token)) return true; + if ("false".equalsIgnoreCase(token)) return false; + if ("1".equals(token)) return true; + if ("0".equals(token)) return false; + throw new InputMismatchException("attempts to read a 'boolean' value from standard input, " + + "but the next token is \"" + token + "\""); + } + catch (NoSuchElementException e) { + throw new NoSuchElementException("attempts to read a 'boolean' value from standard input, " + + "but no more tokens are available"); + } + + } + + /** + * Reads all remaining tokens from standard input and returns them as an array of strings. + * + * @return all remaining tokens on standard input, as an array of strings + */ + public static String[] readAllStrings() { + // we could use readAll.trim().split(), but that's not consistent + // because trim() uses characters 0x00..0x20 as whitespace + String[] tokens = WHITESPACE_PATTERN.split(readAll()); + if (tokens.length == 0 || tokens[0].length() > 0) + return tokens; + + // don't include first token if it is leading whitespace + String[] decapitokens = new String[tokens.length-1]; + for (int i = 0; i < tokens.length - 1; i++) + decapitokens[i] = tokens[i+1]; + return decapitokens; + } + + /** + * Reads all remaining lines from standard input and returns them as an array of strings. + * @return all remaining lines on standard input, as an array of strings + */ + public static String[] readAllLines() { + ArrayList lines = new ArrayList(); + while (hasNextLine()) { + lines.add(readLine()); + } + return lines.toArray(new String[0]); + } + + /** + * Reads all remaining tokens from standard input, parses them as integers, and returns + * them as an array of integers. + * @return all remaining integers on standard input, as an array + * @throws InputMismatchException if any token cannot be parsed as an {@code int} + */ + public static int[] readAllInts() { + String[] fields = readAllStrings(); + int[] vals = new int[fields.length]; + for (int i = 0; i < fields.length; i++) + vals[i] = Integer.parseInt(fields[i]); + return vals; + } + + /** + * Reads all remaining tokens from standard input, parses them as longs, and returns + * them as an array of longs. + * @return all remaining longs on standard input, as an array + * @throws InputMismatchException if any token cannot be parsed as a {@code long} + */ + public static long[] readAllLongs() { + String[] fields = readAllStrings(); + long[] vals = new long[fields.length]; + for (int i = 0; i < fields.length; i++) + vals[i] = Long.parseLong(fields[i]); + return vals; + } + + /** + * Reads all remaining tokens from standard input, parses them as doubles, and returns + * them as an array of doubles. + * @return all remaining doubles on standard input, as an array + * @throws InputMismatchException if any token cannot be parsed as a {@code double} + */ + public static double[] readAllDoubles() { + String[] fields = readAllStrings(); + double[] vals = new double[fields.length]; + for (int i = 0; i < fields.length; i++) + vals[i] = Double.parseDouble(fields[i]); + return vals; + } + + //// end: section (2 of 2) of code duplicated from In to StdIn + + // do this once when StdIn is initialized + static { + resync(); + } + + /** + * If StdIn changes, use this to reinitialize the scanner. + */ + private static void resync() { + setScanner(new Scanner(new java.io.BufferedInputStream(System.in), CHARSET_NAME)); + } + + private static void setScanner(Scanner scanner) { + StdIn.scanner = scanner; + StdIn.scanner.useLocale(LOCALE); + } + + /** + * Reads all remaining tokens, parses them as integers, and returns + * them as an array of integers. + * @return all remaining integers, as an array + * @throws InputMismatchException if any token cannot be parsed as an {@code int} + * @deprecated Replaced by {@link #readAllInts()}. + */ + @Deprecated + public static int[] readInts() { + return readAllInts(); + } + + /** + * Reads all remaining tokens, parses them as doubles, and returns + * them as an array of doubles. + * @return all remaining doubles, as an array + * @throws InputMismatchException if any token cannot be parsed as a {@code double} + * @deprecated Replaced by {@link #readAllDoubles()}. + */ + @Deprecated + public static double[] readDoubles() { + return readAllDoubles(); + } + + /** + * Reads all remaining tokens and returns them as an array of strings. + * @return all remaining tokens, as an array of strings + * @deprecated Replaced by {@link #readAllStrings()}. + */ + @Deprecated + public static String[] readStrings() { + return readAllStrings(); + } + + + /** + * Interactive test of basic functionality. + * + * @param args the command-line arguments + */ + public static void main(String[] args) { + + StdOut.print("Type a string: "); + String s = StdIn.readString(); + StdOut.println("Your string was: " + s); + StdOut.println(); + + StdOut.print("Type an int: "); + int a = StdIn.readInt(); + StdOut.println("Your int was: " + a); + StdOut.println(); + + StdOut.print("Type a boolean: "); + boolean b = StdIn.readBoolean(); + StdOut.println("Your boolean was: " + b); + StdOut.println(); + + StdOut.print("Type a double: "); + double c = StdIn.readDouble(); + StdOut.println("Your double was: " + c); + StdOut.println(); + } + +} diff --git a/common/StdOut.java b/common/StdOut.java new file mode 100644 index 0000000..59ad5f4 --- /dev/null +++ b/common/StdOut.java @@ -0,0 +1,312 @@ +package common; +/****************************************************************************** + * Compilation: javac StdOut.java + * Execution: java StdOut + * Dependencies: none + * + * Writes data of various types to standard output. + * + ******************************************************************************/ + +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Locale; + +/** + * The {@code StdOut} class provides static methods for printing strings + * and numbers to standard output. + * + *

Getting started. + * To use this class, you must have {@code StdOut.class} in your + * Java classpath. If you used our autoinstaller, you should be all set. + * Otherwise, either download + * stdlib.jar + * and add to your Java classpath or download + * StdOut.java + * and put a copy in your working directory. + *

+ * Here is an example program that uses {@code StdOut}: + *

+ *   public class TestStdOut {
+ *       public static void main(String[] args) {
+ *           int a = 17;
+ *           int b = 23;
+ *           int sum = a + b;
+ *           StdOut.println("Hello, World");
+ *           StdOut.printf("%d + %d = %d\n", a, b, sum);
+ *       }
+ *   }
+ *  
+ *

+ * Differences with System.out. + * The behavior of {@code StdOut} is similar to that of {@link System#out}, + * but there are a few technical differences: + *

+ *

+ * Reference. + * For additional documentation, + * see Section 1.5 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + */ +public final class StdOut { + + // force Unicode UTF-8 encoding; otherwise it's system dependent + private static final Charset CHARSET = StandardCharsets.UTF_8; + + // assume language = English, country = US for consistency with StdIn + private static final Locale LOCALE = Locale.US; + + // send output here + private static PrintWriter out; + + // this is called before invoking any methods + static { + out = new PrintWriter(new OutputStreamWriter(System.out, CHARSET), true); + } + + // don't instantiate + private StdOut() { } + + /** + * Terminates the current line by printing the line-separator string. + */ + public static void println() { + out.println(); + } + + /** + * Prints an object to this output stream and then terminates the line. + * + * @param x the object to print + */ + public static void println(Object x) { + out.println(x); + } + + /** + * Prints a boolean to standard output and then terminates the line. + * + * @param x the boolean to print + */ + public static void println(boolean x) { + out.println(x); + } + + /** + * Prints a character to standard output and then terminates the line. + * + * @param x the character to print + */ + public static void println(char x) { + out.println(x); + } + + /** + * Prints a double to standard output and then terminates the line. + * + * @param x the double to print + */ + public static void println(double x) { + out.println(x); + } + + /** + * Prints an integer to standard output and then terminates the line. + * + * @param x the integer to print + */ + public static void println(float x) { + out.println(x); + } + + /** + * Prints an integer to standard output and then terminates the line. + * + * @param x the integer to print + */ + public static void println(int x) { + out.println(x); + } + + /** + * Prints a long to standard output and then terminates the line. + * + * @param x the long to print + */ + public static void println(long x) { + out.println(x); + } + + /** + * Prints a short integer to standard output and then terminates the line. + * + * @param x the short to print + */ + public static void println(short x) { + out.println(x); + } + + /** + * Prints a byte to standard output and then terminates the line. + *

+ * To write binary data, see {@link BinaryStdOut}. + * + * @param x the byte to print + */ + public static void println(byte x) { + out.println(x); + } + + /** + * Flushes standard output. + */ + public static void print() { + out.flush(); + } + + /** + * Prints an object to standard output and flushes standard output. + * + * @param x the object to print + */ + public static void print(Object x) { + out.print(x); + out.flush(); + } + + /** + * Prints a boolean to standard output and flushes standard output. + * + * @param x the boolean to print + */ + public static void print(boolean x) { + out.print(x); + out.flush(); + } + + /** + * Prints a character to standard output and flushes standard output. + * + * @param x the character to print + */ + public static void print(char x) { + out.print(x); + out.flush(); + } + + /** + * Prints a double to standard output and flushes standard output. + * + * @param x the double to print + */ + public static void print(double x) { + out.print(x); + out.flush(); + } + + /** + * Prints a float to standard output and flushes standard output. + * + * @param x the float to print + */ + public static void print(float x) { + out.print(x); + out.flush(); + } + + /** + * Prints an integer to standard output and flushes standard output. + * + * @param x the integer to print + */ + public static void print(int x) { + out.print(x); + out.flush(); + } + + /** + * Prints a long integer to standard output and flushes standard output. + * + * @param x the long integer to print + */ + public static void print(long x) { + out.print(x); + out.flush(); + } + + /** + * Prints a short integer to standard output and flushes standard output. + * + * @param x the short integer to print + */ + public static void print(short x) { + out.print(x); + out.flush(); + } + + /** + * Prints a byte to standard output and flushes standard output. + * + * @param x the byte to print + */ + public static void print(byte x) { + out.print(x); + out.flush(); + } + + /** + * Prints a formatted string to standard output, using the specified format + * string and arguments, and then flushes standard output. + * + * + * @param format the format string + * @param args the arguments accompanying the format string + */ + public static void printf(String format, Object... args) { + out.printf(LOCALE, format, args); + out.flush(); + } + + /** + * Prints a formatted string to standard output, using the locale and + * the specified format string and arguments; then flushes standard output. + * + * @param locale the locale + * @param format the format string + * @param args the arguments accompanying the format string + */ + public static void printf(Locale locale, String format, Object... args) { + out.printf(locale, format, args); + out.flush(); + } + + /** + * Unit tests some methods in {@code StdOut}. + * + * @param args the command-line arguments + */ + public static void main(String[] args) { + + // write to stdout + StdOut.println("Test"); + StdOut.println(17); + StdOut.println(true); + StdOut.printf("%.6f\n", 1.0/7.0); + } + +} diff --git a/common/StdRandom.java b/common/StdRandom.java new file mode 100644 index 0000000..d517c71 --- /dev/null +++ b/common/StdRandom.java @@ -0,0 +1,737 @@ +package common; +/****************************************************************************** + * Compilation: javac StdRandom.java + * Execution: java StdRandom + * Dependencies: StdOut.java + * + * A library of static methods to generate pseudo-random numbers from + * different distributions (bernoulli, uniform, gaussian, discrete, + * and exponential). Also includes a method for shuffling an array. + * + * + * % java StdRandom 5 + * seed = 1316600602069 + * 59 16.81826 true 8.83954 0 + * 32 91.32098 true 9.11026 0 + * 35 10.11874 true 8.95396 3 + * 92 32.88401 true 8.87089 0 + * 72 92.55791 true 9.46241 0 + * + * % java StdRandom 5 + * seed = 1316600616575 + * 96 60.17070 true 8.72821 0 + * 79 32.01607 true 8.58159 0 + * 81 59.49065 true 9.10423 1 + * 96 51.65818 true 9.02102 0 + * 99 17.55771 true 8.99762 0 + * + * % java StdRandom 5 1316600616575 + * seed = 1316600616575 + * 96 60.17070 true 8.72821 0 + * 79 32.01607 true 8.58159 0 + * 81 59.49065 true 9.10423 1 + * 96 51.65818 true 9.02102 0 + * 99 17.55771 true 8.99762 0 + * + * + * Remark + * ------ + * - Relies on randomness of nextDouble() method in java.util.Random + * to generate pseudo-random numbers in [0, 1). + * + * - This library allows you to set and get the pseudo-random number seed. + * + * - See http://www.honeylocust.com/RngPack/ for an industrial + * strength random number generator in Java. + * + ******************************************************************************/ + +import java.util.Random; + +/** + * The {@code StdRandom} class provides static methods for generating + * random number from various discrete and continuous distributions, + * including uniform, Bernoulli, geometric, Gaussian, exponential, Pareto, + * Poisson, and Cauchy. It also provides method for shuffling an + * array or subarray and generating random permutations. + * + *

Conventions. + * By convention, all intervals are half open. For example, + * uniformDouble(-1.0, 1.0) returns a random number between + * -1.0 (inclusive) and 1.0 (exclusive). + * Similarly, shuffle(a, lo, hi) shuffles the hi - lo + * elements in the array a[], starting at index lo + * (inclusive) and ending at index hi (exclusive). + * + *

Performance. + * The methods all take constant expected time, except those that involve arrays. + * The shuffle method takes time linear in the subarray to be shuffled; + * the discrete methods take time linear in the length of the argument + * array. + * + *

Additional information. + * For additional documentation, + * see Section 2.2 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + */ +public final class StdRandom { + + private static Random random; // pseudo-random number generator + private static long seed; // pseudo-random number generator seed + + // static initializer + static { + // this is how the seed was set in Java 1.4 + seed = System.currentTimeMillis(); + random = new Random(seed); + } + + // don't instantiate + private StdRandom() { } + + /** + * Sets the seed of the pseudo-random number generator. + * This method enables you to produce the same sequence of "random" + * number for each execution of the program. + * Ordinarily, you should call this method at most once per program. + * + * @param s the seed + */ + public static void setSeed(long s) { + seed = s; + random = new Random(seed); + } + + /** + * Returns the seed of the pseudo-random number generator. + * + * @return the seed + */ + public static long getSeed() { + return seed; + } + + /** + * Returns a random real number uniformly in [0, 1). + * + * @return a random real number uniformly in [0, 1) + * @deprecated Replaced by {@link #uniformDouble()}. + */ + @Deprecated + public static double uniform() { + return uniformDouble(); + } + + /** + * Returns a random real number uniformly in [0, 1). + * + * @return a random real number uniformly in [0, 1) + */ + public static double uniformDouble() { + return random.nextDouble(); + } + + /** + * Returns a random integer uniformly in [0, n). + * + * @param n number of possible integers + * @return a random integer uniformly between 0 (inclusive) and {@code n} (exclusive) + * @throws IllegalArgumentException if {@code n <= 0} + * @deprecated Replaced by {@link #uniformInt(int n)}. + */ + @Deprecated + public static int uniform(int n) { + return uniformInt(n); + } + + /** + * Returns a random integer uniformly in [0, n). + * + * @param n number of possible integers + * @return a random integer uniformly between 0 (inclusive) and {@code n} (exclusive) + * @throws IllegalArgumentException if {@code n <= 0} + */ + public static int uniformInt(int n) { + if (n <= 0) throw new IllegalArgumentException("argument must be positive: " + n); + return random.nextInt(n); + } + + /** + * Returns a random long integer uniformly in [0, n). + * + * @param n number of possible {@code long} integers + * @return a random long integer uniformly between 0 (inclusive) and {@code n} (exclusive) + * @throws IllegalArgumentException if {@code n <= 0} + * @deprecated Replaced by {@link #uniformLong(long n)}. + */ + @Deprecated + public static long uniform(long n) { + return uniformLong(n); + } + + /** + * Returns a random long integer uniformly in [0, n). + * + * @param n number of possible {@code long} integers + * @return a random long integer uniformly between 0 (inclusive) and {@code n} (exclusive) + * @throws IllegalArgumentException if {@code n <= 0} + */ + public static long uniformLong(long n) { + if (n <= 0L) throw new IllegalArgumentException("argument must be positive: " + n); + + // https://docs.oracle.com/javase/8/docs/api/java/util/Random.html#longs-long-long-long- + long r = random.nextLong(); + long m = n - 1; + + // power of two + if ((n & m) == 0L) { + return r & m; + } + + // reject over-represented candidates + long u = r >>> 1; + while (u + m - (r = u % n) < 0L) { + u = random.nextLong() >>> 1; + } + return r; + } + + /////////////////////////////////////////////////////////////////////////// + // STATIC METHODS BELOW RELY ON JAVA.UTIL.RANDOM ONLY INDIRECTLY VIA + // THE STATIC METHODS ABOVE. + /////////////////////////////////////////////////////////////////////////// + + /** + * Returns a random real number uniformly in [0, 1). + * + * @return a random real number uniformly in [0, 1) + * @deprecated Replaced by {@link #uniformDouble()}. + */ + @Deprecated + public static double random() { + return uniformDouble(); + } + + /** + * Returns a random integer uniformly in [a, b). + * + * @param a the left endpoint + * @param b the right endpoint + * @return a random integer uniformly in [a, b) + * @throws IllegalArgumentException if {@code b <= a} + * @throws IllegalArgumentException if {@code b - a >= Integer.MAX_VALUE} + * @deprecated Replaced by {@link #uniformInt(int a, int b)}. + */ + @Deprecated + public static int uniform(int a, int b) { + return uniformInt(a, b); + } + + /** + * Returns a random integer uniformly in [a, b). + * + * @param a the left endpoint + * @param b the right endpoint + * @return a random integer uniformly in [a, b) + * @throws IllegalArgumentException if {@code b <= a} + * @throws IllegalArgumentException if {@code b - a >= Integer.MAX_VALUE} + */ + public static int uniformInt(int a, int b) { + if ((b <= a) || ((long) b - a >= Integer.MAX_VALUE)) { + throw new IllegalArgumentException("invalid range: [" + a + ", " + b + ")"); + } + return a + uniform(b - a); + } + + /** + * Returns a random real number uniformly in [a, b). + * + * @param a the left endpoint + * @param b the right endpoint + * @return a random real number uniformly in [a, b) + * @throws IllegalArgumentException unless {@code a < b} + * @deprecated Replaced by {@link #uniformDouble(double a, double b)}. + */ + @Deprecated + public static double uniform(double a, double b) { + return uniformDouble(a, b); + } + + /** + * Returns a random real number uniformly in [a, b). + * + * @param a the left endpoint + * @param b the right endpoint + * @return a random real number uniformly in [a, b) + * @throws IllegalArgumentException unless {@code a < b} + */ + public static double uniformDouble(double a, double b) { + if (!(a < b)) { + throw new IllegalArgumentException("invalid range: [" + a + ", " + b + ")"); + } + return a + uniform() * (b-a); + } + + /** + * Returns a random boolean from a Bernoulli distribution with success + * probability p. + * + * @param p the probability of returning {@code true} + * @return {@code true} with probability {@code p} and + * {@code false} with probability {@code 1 - p} + * @throws IllegalArgumentException unless {@code 0} ≤ {@code p} ≤ {@code 1.0} + */ + public static boolean bernoulli(double p) { + if (!(p >= 0.0 && p <= 1.0)) + throw new IllegalArgumentException("probability p must be between 0.0 and 1.0: " + p); + return uniformDouble() < p; + } + + /** + * Returns a random boolean from a Bernoulli distribution with success + * probability 1/2. + * + * @return {@code true} with probability 1/2 and + * {@code false} with probability 1/2 + */ + public static boolean bernoulli() { + return bernoulli(0.5); + } + + /** + * Returns a random real number from a standard Gaussian distribution. + * + * @return a random real number from a standard Gaussian distribution + * (mean 0 and standard deviation 1). + */ + public static double gaussian() { + // use the polar form of the Box-Muller transform + double r, x, y; + do { + x = uniformDouble(-1.0, 1.0); + y = uniformDouble(-1.0, 1.0); + r = x*x + y*y; + } while (r >= 1 || r == 0); + return x * Math.sqrt(-2 * Math.log(r) / r); + + // Remark: y * Math.sqrt(-2 * Math.log(r) / r) + // is an independent random gaussian + } + + /** + * Returns a random real number from a Gaussian distribution with mean μ + * and standard deviation σ. + * + * @param mu the mean + * @param sigma the standard deviation + * @return a real number distributed according to the Gaussian distribution + * with mean {@code mu} and standard deviation {@code sigma} + */ + public static double gaussian(double mu, double sigma) { + return mu + sigma * gaussian(); + } + + /** + * Returns a random integer from a geometric distribution with success + * probability p. + * The integer represents the number of independent trials + * before the first success. + * + * @param p the parameter of the geometric distribution + * @return a random integer from a geometric distribution with success + * probability {@code p}; or {@code Integer.MAX_VALUE} if + * {@code p} is (nearly) equal to {@code 1.0}. + * @throws IllegalArgumentException unless {@code p >= 0.0} and {@code p <= 1.0} + */ + public static int geometric(double p) { + if (!(p >= 0)) { + throw new IllegalArgumentException("probability p must be greater than 0: " + p); + } + if (!(p <= 1.0)) { + throw new IllegalArgumentException("probability p must not be larger than 1: " + p); + } + // using algorithm given by Knuth + return (int) Math.ceil(Math.log(uniformDouble()) / Math.log(1.0 - p)); + } + + /** + * Returns a random integer from a Poisson distribution with mean λ. + * + * @param lambda the mean of the Poisson distribution + * @return a random integer from a Poisson distribution with mean {@code lambda} + * @throws IllegalArgumentException unless {@code lambda > 0.0} and not infinite + */ + public static int poisson(double lambda) { + if (!(lambda > 0.0)) + throw new IllegalArgumentException("lambda must be positive: " + lambda); + if (Double.isInfinite(lambda)) + throw new IllegalArgumentException("lambda must not be infinite: " + lambda); + // using algorithm given by Knuth + // see http://en.wikipedia.org/wiki/Poisson_distribution + int k = 0; + double p = 1.0; + double expLambda = Math.exp(-lambda); + do { + k++; + p *= uniformDouble(); + } while (p >= expLambda); + return k-1; + } + + /** + * Returns a random real number from the standard Pareto distribution. + * + * @return a random real number from the standard Pareto distribution + */ + public static double pareto() { + return pareto(1.0); + } + + /** + * Returns a random real number from a Pareto distribution with + * shape parameter α. + * + * @param alpha shape parameter + * @return a random real number from a Pareto distribution with shape + * parameter {@code alpha} + * @throws IllegalArgumentException unless {@code alpha > 0.0} + */ + public static double pareto(double alpha) { + if (!(alpha > 0.0)) + throw new IllegalArgumentException("alpha must be positive: " + alpha); + return Math.pow(1 - uniformDouble(), -1.0 / alpha) - 1.0; + } + + /** + * Returns a random real number from the Cauchy distribution. + * + * @return a random real number from the Cauchy distribution. + */ + public static double cauchy() { + return Math.tan(Math.PI * (uniformDouble() - 0.5)); + } + + /** + * Returns a random integer from the specified discrete distribution. + * + * @param probabilities the probability of occurrence of each integer + * @return a random integer from a discrete distribution: + * {@code i} with probability {@code probabilities[i]} + * @throws IllegalArgumentException if {@code probabilities} is {@code null} + * @throws IllegalArgumentException if sum of array entries is not (very nearly) equal to {@code 1.0} + * @throws IllegalArgumentException unless {@code probabilities[i] >= 0.0} for each index {@code i} + */ + public static int discrete(double[] probabilities) { + if (probabilities == null) throw new IllegalArgumentException("argument array must not be null"); + double EPSILON = 1.0E-14; + double sum = 0.0; + for (int i = 0; i < probabilities.length; i++) { + if (!(probabilities[i] >= 0.0)) + throw new IllegalArgumentException("array entry " + i + " must be non-negative: " + probabilities[i]); + sum += probabilities[i]; + } + if (sum > 1.0 + EPSILON || sum < 1.0 - EPSILON) + throw new IllegalArgumentException("sum of array entries does not approximately equal 1.0: " + sum); + + // the for loop may not return a value when both r is (nearly) 1.0 and when the + // cumulative sum is less than 1.0 (as a result of floating-point roundoff error) + while (true) { + double r = uniformDouble(); + sum = 0.0; + for (int i = 0; i < probabilities.length; i++) { + sum = sum + probabilities[i]; + if (sum > r) return i; + } + } + } + + /** + * Returns a random integer from the specified discrete distribution. + * + * @param frequencies the frequency of occurrence of each integer + * @return a random integer from a discrete distribution: + * {@code i} with probability proportional to {@code frequencies[i]} + * @throws IllegalArgumentException if {@code frequencies} is {@code null} + * @throws IllegalArgumentException if all array entries are {@code 0} + * @throws IllegalArgumentException if {@code frequencies[i]} is negative for any index {@code i} + * @throws IllegalArgumentException if sum of frequencies exceeds {@code Integer.MAX_VALUE} (231 - 1) + */ + public static int discrete(int[] frequencies) { + if (frequencies == null) throw new IllegalArgumentException("argument array must not be null"); + long sum = 0; + for (int i = 0; i < frequencies.length; i++) { + if (frequencies[i] < 0) + throw new IllegalArgumentException("array entry " + i + " must be non-negative: " + frequencies[i]); + sum += frequencies[i]; + } + if (sum == 0) + throw new IllegalArgumentException("at least one array entry must be positive"); + if (sum >= Integer.MAX_VALUE) + throw new IllegalArgumentException("sum of frequencies overflows an int"); + + // pick index i with probability proportional to frequency + double r = uniformInt((int) sum); + sum = 0; + for (int i = 0; i < frequencies.length; i++) { + sum += frequencies[i]; + if (sum > r) return i; + } + + // can't reach here + assert false; + return -1; + } + + /** + * Returns a random real number from an exponential distribution + * with rate λ. + * + * @param lambda the rate of the exponential distribution + * @return a random real number from an exponential distribution with + * rate {@code lambda} + * @throws IllegalArgumentException unless {@code lambda > 0.0} + */ + public static double exponential(double lambda) { + if (!(lambda > 0.0)) + throw new IllegalArgumentException("lambda must be positive: " + lambda); + return -Math.log(1 - uniformDouble()) / lambda; + } + + /** + * Returns a random real number from an exponential distribution + * with rate λ. + * + * @param lambda the rate of the exponential distribution + * @return a random real number from an exponential distribution with + * rate {@code lambda} + * @throws IllegalArgumentException unless {@code lambda > 0.0} + * @deprecated Replaced by {@link #exponential(double)}. + */ + @Deprecated + public static double exp(double lambda) { + return exponential(lambda); + } + + /** + * Rearranges the elements of the specified array in uniformly random order. + * + * @param a the array to shuffle + * @throws IllegalArgumentException if {@code a} is {@code null} + */ + public static void shuffle(Object[] a) { + validateNotNull(a); + int n = a.length; + for (int i = 0; i < n; i++) { + int r = i + uniformInt(n-i); // between i and n-1 + Object temp = a[i]; + a[i] = a[r]; + a[r] = temp; + } + } + + /** + * Rearranges the elements of the specified array in uniformly random order. + * + * @param a the array to shuffle + * @throws IllegalArgumentException if {@code a} is {@code null} + */ + public static void shuffle(double[] a) { + validateNotNull(a); + int n = a.length; + for (int i = 0; i < n; i++) { + int r = i + uniformInt(n-i); // between i and n-1 + double temp = a[i]; + a[i] = a[r]; + a[r] = temp; + } + } + + /** + * Rearranges the elements of the specified array in uniformly random order. + * + * @param a the array to shuffle + * @throws IllegalArgumentException if {@code a} is {@code null} + */ + public static void shuffle(int[] a) { + validateNotNull(a); + int n = a.length; + for (int i = 0; i < n; i++) { + int r = i + uniformInt(n-i); // between i and n-1 + int temp = a[i]; + a[i] = a[r]; + a[r] = temp; + } + } + + /** + * Rearranges the elements of the specified array in uniformly random order. + * + * @param a the array to shuffle + * @throws IllegalArgumentException if {@code a} is {@code null} + */ + public static void shuffle(char[] a) { + validateNotNull(a); + int n = a.length; + for (int i = 0; i < n; i++) { + int r = i + uniformInt(n-i); // between i and n-1 + char temp = a[i]; + a[i] = a[r]; + a[r] = temp; + } + } + + /** + * Rearranges the elements of the specified subarray in uniformly random order. + * + * @param a the array to shuffle + * @param lo the left endpoint (inclusive) + * @param hi the right endpoint (exclusive) + * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} + * + */ + public static void shuffle(Object[] a, int lo, int hi) { + validateNotNull(a); + validateSubarrayIndices(lo, hi, a.length); + + for (int i = lo; i < hi; i++) { + int r = i + uniformInt(hi-i); // between i and hi-1 + Object temp = a[i]; + a[i] = a[r]; + a[r] = temp; + } + } + + /** + * Rearranges the elements of the specified subarray in uniformly random order. + * + * @param a the array to shuffle + * @param lo the left endpoint (inclusive) + * @param hi the right endpoint (exclusive) + * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} + */ + public static void shuffle(double[] a, int lo, int hi) { + validateNotNull(a); + validateSubarrayIndices(lo, hi, a.length); + + for (int i = lo; i < hi; i++) { + int r = i + uniformInt(hi-i); // between i and hi-1 + double temp = a[i]; + a[i] = a[r]; + a[r] = temp; + } + } + + /** + * Rearranges the elements of the specified subarray in uniformly random order. + * + * @param a the array to shuffle + * @param lo the left endpoint (inclusive) + * @param hi the right endpoint (exclusive) + * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} + */ + public static void shuffle(int[] a, int lo, int hi) { + validateNotNull(a); + validateSubarrayIndices(lo, hi, a.length); + + for (int i = lo; i < hi; i++) { + int r = i + uniformInt(hi-i); // between i and hi-1 + int temp = a[i]; + a[i] = a[r]; + a[r] = temp; + } + } + + /** + * Returns a uniformly random permutation of n elements. + * + * @param n number of elements + * @throws IllegalArgumentException if {@code n} is negative + * @return an array of length {@code n} that is a uniformly random permutation + * of {@code 0}, {@code 1}, ..., {@code n-1} + */ + public static int[] permutation(int n) { + if (n < 0) throw new IllegalArgumentException("n must be non-negative: " + n); + int[] perm = new int[n]; + for (int i = 0; i < n; i++) + perm[i] = i; + shuffle(perm); + return perm; + } + + /** + * Returns a uniformly random permutation of k of n elements. + * + * @param n number of elements + * @param k number of elements to select + * @throws IllegalArgumentException if {@code n} is negative + * @throws IllegalArgumentException unless {@code 0 <= k <= n} + * @return an array of length {@code k} that is a uniformly random permutation + * of {@code k} of the elements from {@code 0}, {@code 1}, ..., {@code n-1} + */ + public static int[] permutation(int n, int k) { + if (n < 0) throw new IllegalArgumentException("n must be non-negative: " + n); + if (k < 0 || k > n) throw new IllegalArgumentException("k must be between 0 and n: " + k); + int[] perm = new int[k]; + for (int i = 0; i < k; i++) { + int r = uniformInt(i+1); // between 0 and i + perm[i] = perm[r]; + perm[r] = i; + } + for (int i = k; i < n; i++) { + int r = uniformInt(i+1); // between 0 and i + if (r < k) perm[r] = i; + } + return perm; + } + + // throw an IllegalArgumentException if x is null + // (x can be of type Object[], double[], int[], ...) + private static void validateNotNull(Object x) { + if (x == null) { + throw new IllegalArgumentException("argument must not be null"); + } + } + + // throw an exception unless 0 <= lo <= hi <= length + private static void validateSubarrayIndices(int lo, int hi, int length) { + if (lo < 0 || hi > length || lo > hi) { + throw new IllegalArgumentException("subarray indices out of bounds: [" + lo + ", " + hi + ")"); + } + } + + /** + * Unit tests the methods in this class. + * + * @param args the command-line arguments + */ + public static void main(String[] args) { + int n = Integer.parseInt(args[0]); + if (args.length == 2) StdRandom.setSeed(Long.parseLong(args[1])); + double[] probabilities = { 0.5, 0.3, 0.1, 0.1 }; + int[] frequencies = { 5, 3, 1, 1 }; + String[] a = "A B C D E F G".split(" "); + + StdOut.println("seed = " + StdRandom.getSeed()); + for (int i = 0; i < n; i++) { + StdOut.printf("%2d ", uniformInt(100)); + StdOut.printf("%8.5f ", uniformDouble(10.0, 99.0)); + StdOut.printf("%5b ", bernoulli(0.5)); + StdOut.printf("%7.5f ", gaussian(9.0, 0.2)); + StdOut.printf("%1d ", discrete(probabilities)); + StdOut.printf("%1d ", discrete(frequencies)); + StdOut.printf("%11d ", uniformLong(100000000000L)); + StdRandom.shuffle(a); + for (String s : a) + StdOut.print(s); + StdOut.println(); + } + } + +} diff --git a/week3/GamblerPlot.java b/week3/GamblerPlot.java index aba343a..b486a7a 100644 --- a/week3/GamblerPlot.java +++ b/week3/GamblerPlot.java @@ -17,7 +17,7 @@ public class GamblerPlot { else cash--; - GamblerPlot.printCash(cash); + printCash(cash); } boolean won = cash == goal; diff --git a/week3/RollLoadedDie.java b/week3/RollLoadedDie.java index 5e2d290..46abf26 100644 --- a/week3/RollLoadedDie.java +++ b/week3/RollLoadedDie.java @@ -2,8 +2,9 @@ public class RollLoadedDie { public static void main(String[] args) { double rand8 = Math.random() * 8.; // floor returns double. Round gives long for double input, so conversion needed - int random = (int)(Math.round(Math.floor(rand8)) + 1); + int random = (int)(Math.floor(rand8) + 1); // 1 - 8 to 1 - 6, with 6 receiving the probabilities of 7 - 8 + // 1 - 5 have their original probabilities, meaning each have 1/8 chance of being selected, while 6 is 3/8 random = Math.min(random, 6); System.out.println(random); } diff --git a/week3/doc.typ b/week3/doc.typ index 7eb2a05..5617400 100644 --- a/week3/doc.typ +++ b/week3/doc.typ @@ -27,4 +27,8 @@ the (fixed) probability that the gambler wins each bet. Use your program to try to learn how this probability affects the chance of winning and the expected number of bets. Try a value of $p$ close to $0.5$ (say, $0.48$). +Small changes of probability affect win % a lot, for example the chance of +doubling money from $100$ to $200$ is roughly 50% for $p=0.5$, but falls to +less than 5% for $p=0.49$ and 0% below that. + #embedClass(name: "Gambler") \ No newline at end of file diff --git a/week4/Card.java b/week4/Card.java index 898ca5d..b428bb4 100644 --- a/week4/Card.java +++ b/week4/Card.java @@ -1,21 +1,22 @@ public class Card { - public static final String CLUBS = "♣"; - public static final String DIAMONDS = "♦"; - public static final String HEARTS = "♥"; - public static final String SPADES = "♠"; - + public enum Suit { Clubs, Diamonds, Hearts, Spades; + + public static final String CLUBS = "♣"; + public static final String DIAMONDS = "♦"; + public static final String HEARTS = "♥"; + public static final String SPADES = "♠"; public String sprint() { switch(this) { - case Clubs: return Card.CLUBS; - case Diamonds: return Card.DIAMONDS; - case Hearts: return Card.HEARTS; - case Spades: return Card.SPADES; + case Clubs: return CLUBS; + case Diamonds: return DIAMONDS; + case Hearts: return HEARTS; + case Spades: return SPADES; } throw new NullPointerException(); } @@ -29,81 +30,95 @@ public class Card { this.value = value; } - public static Card fromNumbers(int suit, int value) { - return new Card(Suit.values()[suit], value); - } - - public String sprintValue() { + // Print a single character for the value + // Only a single character is returned so that we can format + // the card output correctly and easily + public char sprintValue() { + assert this.value > 0 && this.value < 14; if(this.value == 1) { - return "A"; + return 'A'; } else if(this.value == 11) { - return "J"; + return 'J'; } else if(this.value == 12) { - return "Q"; + return 'Q'; } else if(this.value == 13) { - return "K"; + return 'K'; } else if(this.value == 10) { - return "⒑"; + // UTF-8 character that looks like a 10 + // but uses only one character width + return '⒑'; } - return Integer.toString(this.value); + // asserted 0 < x < 14, we handled 1, 10-13 + // the only valid values here are 2-9 + return Integer.toString(this.value).charAt(0); } + // Print the card to a string to be later processed (or printed). + // Approximates 'normal' card deck look + // Numbered cards have their suit symbol repeated based on their value. + // Face cards are empty. public String sprintCard() { var output = ""; var value = this.sprintValue(); var suit = this.suit.sprint(); - output += value; - + // Generate the top (and bottom) of a card + // this will show the value of the card on each edge + var top = ""; + top += value; if(this.value > 10) { - output += " "; + top += " "; } else { - output += this.value >= 4 ? suit : " "; - output += this.value < 4 && this.value > 1 ? suit : " "; - output += this.value >= 4 ? suit : " "; + // and for numbered cards, show the suit characters + top += this.value >= 4 ? suit : " "; + top += this.value < 4 && this.value > 1 ? suit : " "; + top += this.value >= 4 ? suit : " "; } - output += value; - output += "\n"; + top += value; + top += "\n"; + + output += top; if(this.value > 10) { + // face cards get suits on the side, right above and under their values + // numbered cards are empty on their sides output += suit + " " + suit + "\n"; output += suit + " " + suit + "\n"; } else { + // normal cards have either 3 or 4 rows of suits in 1-3 columns, + // with the middle one sometimes floating. + // we have to have a set size and can't have floating characters, + // so this is a best effort approximation + // instead of using 3 rows we have a gap in the 3rd row output += " "; output += this.value >= 6 ? suit : " "; - output += this.value % 2 == 1 || this.value >= 8 ? suit : " "; + // odd or 10 + output += this.value % 2 == 1 || this.value == 10 ? suit : " "; output += this.value >= 6 ? suit : " "; output += " "; output += "\n"; output += " "; - output += this.value >= 9 ? suit : " "; - output += this.value == 8 || this.value == 10 ? suit : " "; - output += this.value >= 9 ? suit : " "; + output += this.value >= 8 ? suit : " "; + output += this.value == 10 ? suit : " "; + output += this.value >= 8 ? suit : " "; output += " "; output += "\n"; } - - output += value; - - if(this.value > 10) { - output += " "; - } else { - output += this.value >= 4 ? suit : " "; - output += this.value < 4 && this.value > 1 ? suit : " "; - output += this.value >= 4 ? suit : " "; - } - output += value; - output += "\n"; + output += top; return output; } + // Shows cards next to each other (left to right) + // assumes that sprintCard returns the same width for each card (and each row of a card) public static String sprintCards(Card[] cards) { String[] output = { "", "", "", "" }; + // split each card into it's 4 rows + // save the row into relevant output for(var i = 0; i < cards.length; i++) { var cardstr = cards[i].sprintCard().split("\n"); for(var x = 0; x < cardstr.length; x++) { @@ -111,6 +126,7 @@ public class Card { } } + // and join the rows together with a newline var outputstr = ""; for(var i = 0; i < output.length; i++) { outputstr += output[i] + "\n"; diff --git a/week4/Deal.java b/week4/Deal.java index 5a52e0e..c10208a 100644 --- a/week4/Deal.java +++ b/week4/Deal.java @@ -3,19 +3,25 @@ import java.util.Random; public class Deal { public static void main(String[] args) { int handCount = Integer.parseInt(args[0]); + // We have 52 cards, each hand is 5 cards if(handCount > 10) { System.err.println("Too many hands! Maximum is 10.\njava Deal [hands]"); } + // Generate the deck Card[] deck = new Card[52]; for(var i = 0; i < 52; i++) { + // 13 cards for each suit, take advantage of int division var value = (i % 13) + 1; var suit = i / 13; - deck[i] = Card.fromNumbers(suit, value); + deck[i] = new Card(Card.Suit.values()[suit], value); } + // Randomly shuffle the deck Random random = new Random(); for(var i = 0; i < deck.length; i++) { + // swap each item with a random item in the array + // isn't truly random, but we're not using crypto random anyway var second = random.nextInt(deck.length); var a = deck[i]; deck[i] = deck[second]; @@ -24,7 +30,15 @@ public class Deal { for(var handNumber = 0; handNumber < handCount; handNumber++) { var offset = handNumber * 5; - Card[] hand = { deck[offset], deck[offset+1], deck[offset+2], deck[offset+3], deck[offset+4] }; + // since the deck is shuffled, we just take the next 5 cards from the array + // java could use an array slice so that we didn't need to copy all the objects.. + Card[] hand = { + deck[offset], + deck[offset+1], + deck[offset+2], + deck[offset+3], + deck[offset+4] + }; System.out.println(Card.sprintCards(hand)); } } diff --git a/week4/Transpose.java b/week4/Transpose.java new file mode 100644 index 0000000..57db44f --- /dev/null +++ b/week4/Transpose.java @@ -0,0 +1,3 @@ +public class Transpose { + +} diff --git a/week4/doc.typ b/week4/doc.typ index 5ec005a..7fe39d3 100644 --- a/week4/doc.typ +++ b/week4/doc.typ @@ -23,3 +23,18 @@ For the example spreadsheet array in the text, you code would print the followin 85 57 77 32 34 46 59 66 71 29 98 78 76 11 22 54 88 89 24 38 ``` + +Input: + +``` +99 85 98 +98 57 78 +92 77 76 +94 32 11 +99 34 22 +90 46 54 +76 59 88 +92 66 89 +97 71 24 +89 29 38 +``` diff --git a/week4/tests/Transpose/input1.txt b/week4/tests/Transpose/input1.txt new file mode 100644 index 0000000..f010698 --- /dev/null +++ b/week4/tests/Transpose/input1.txt @@ -0,0 +1,10 @@ +99 85 98 +98 57 78 +92 77 76 +94 32 11 +99 34 22 +90 46 54 +76 59 88 +92 66 89 +97 71 24 +89 29 38 \ No newline at end of file diff --git a/week4/tests/Transpose/output1.txt b/week4/tests/Transpose/output1.txt new file mode 100644 index 0000000..744cb60 --- /dev/null +++ b/week4/tests/Transpose/output1.txt @@ -0,0 +1,3 @@ +99 98 92 94 99 90 76 92 97 89 +85 57 77 32 34 46 59 66 71 29 +98 78 76 11 22 54 88 89 24 38 \ No newline at end of file