Commit d476add1 authored by Adam Blank's avatar Adam Blank
Browse files

Initial Commit

parents
No related merge requests found
Pipeline #36403 canceled with stage
Showing with 4395 additions and 0 deletions
+4395 -0
package edu.caltech.cs2.interfaces;
/**
* This interface represents a stack - a data structure that can add elements remove elements from a single end.
* @param <E> Element type
*/
public interface IStack<E> extends Iterable<E> {
/**
* Adds an element to the top of the stack.
* @param e Element to add
* @return True if successful, false otherwise (i.e. fixed data structure is full)
*/
public boolean push(E e);
/**
* Removes and returns the element at the top of the stack. Returns null if stack is empty.
* @return Element at top of the stack, if one exists; null otherwise
*/
public E pop();
/**
* Returns (but does not remove) the element at the top of the stack. Returns null if stack is empty.
* @return Element at top of the stack, if one exists; null otherwise
*/
public E peek();
/**
* Calculates the size of the stack.
* @return Number of elements in the stack
*/
public int size();
}
package edu.caltech.cs2.project03;
import edu.caltech.cs2.datastructures.CircularArrayFixedSizeQueue;
import edu.caltech.cs2.interfaces.IQueue;
import java.util.Random;
public class CircularArrayFixedSizeQueueGuitarString {
public CircularArrayFixedSizeQueueGuitarString(double frequency) {
}
public int length() {
return -1;
}
public void pluck() {
}
public void tic() {
}
public double sample() {
return 0;
}
}
package edu.caltech.cs2.project03;
import edu.caltech.cs2.project03.libraries.StdAudio;
import edu.caltech.cs2.project03.libraries.StdDraw;
import edu.caltech.cs2.datastructures.*;
public class GuitarHero {
private static final String KEYBOARD = "q2we4r5ty7u8i9op-[=zxdcfvgbnjmk,.;/' ";
private static final double CONCERT_A = 440.0;
public static void main(String[] args) {
// Create three arrays of strings
CircularArrayFixedSizeQueueGuitarString[] strings = new CircularArrayFixedSizeQueueGuitarString[KEYBOARD.length()];
// Populate string arrays with respective string types
for (int i = 0; i < KEYBOARD.length(); i++) {
strings[i] = new CircularArrayFixedSizeQueueGuitarString(CONCERT_A * Math.pow(2, (i - 24.0) / 12.0));
}
while (true) {
// check if the user has typed a key; if so, process it
if (StdDraw.hasNextKeyTyped()) {
char key = StdDraw.nextKeyTyped();
int idx = KEYBOARD.indexOf(key);
if (idx != -1) {
strings[idx].pluck();
}
}
// compute the superposition of samples
double sample = 0.0;
for (int i = 0; i < strings.length; i++) {
sample += strings[i].sample();
}
// play the sample on standard audio
StdAudio.play(sample);
// advance the simulation of each guitar string by one step
for (int i = 0; i < strings.length; i++) {
strings[i].tic();
}
}
}
}
package edu.caltech.cs2.project03.libraries;
/******************************************************************************
* Compilation: javac StdAudio.java
* Execution: java StdAudio
* Dependencies: none
*
* Simple library for reading, writing, and manipulating .wav files.
*
*
* Limitations
* -----------
* - Does not seem to work properly when reading .wav files from a .jar file.
* - Assumes the audio is monaural, with sampling rate of 44,100.
*
******************************************************************************/
import javax.sound.sampled.Clip;
// for playing midi sound files on some older systems
import java.applet.Applet;
import java.applet.AudioClip;
import java.net.MalformedURLException;
import java.io.File;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
* <i>Standard audio</i>. This class provides a basic capability for
* creating, reading, and saving audio.
* <p>
* The audio format uses a sampling rate of 44,100 (CD quality audio), 16-bit, monaural.
*
* <p>
* For additional documentation, see <a href="https://introcs.cs.princeton.edu/15inout">Section 1.5</a> of
* <i>Computer Science: An Interdisciplinary Approach</i> by Robert Sedgewick and Kevin Wayne.
*
* @author Robert Sedgewick
* @author Kevin Wayne
*/
public final class StdAudio {
/**
* The sample rate - 44,100 Hz for CD quality audio.
*/
public static final int SAMPLE_RATE = 44100;
private static final int BYTES_PER_SAMPLE = 2; // 16-bit audio
private static final int BITS_PER_SAMPLE = 16; // 16-bit audio
private static final double MAX_16_BIT = Short.MAX_VALUE; // 32,767
private static final int SAMPLE_BUFFER_SIZE = 4096;
private static SourceDataLine line; // to play the sound
private static byte[] buffer; // our internal buffer
private static int bufferSize = 0; // number of samples currently in internal buffer
private StdAudio() {
// can not instantiate
}
// static initializer
static {
init();
}
// open up an audio stream
private static void init() {
try {
// 44,100 samples per second, 16-bit audio, mono, signed PCM, little Endian
AudioFormat format = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, 1, true, false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE);
// the internal buffer is a fraction of the actual buffer size, this choice is arbitrary
// it gets divided because we can't expect the buffered data to line up exactly with when
// the sound card decides to push out its samples.
buffer = new byte[SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE/3];
}
catch (LineUnavailableException e) {
System.out.println(e.getMessage());
}
// no sound gets made before this call
line.start();
}
/**
* Closes standard audio.
*/
public static void close() {
line.drain();
line.stop();
}
/**
* Writes one sample (between -1.0 and +1.0) to standard audio.
* If the sample is outside the range, it will be clipped.
*
* @param sample the sample to play
* @throws IllegalArgumentException if the sample is {@code Double.NaN}
*/
public static void play(double sample) {
// clip if outside [-1, +1]
if (Double.isNaN(sample)) throw new IllegalArgumentException("sample is NaN");
if (sample < -1.0) sample = -1.0;
if (sample > +1.0) sample = +1.0;
// convert to bytes
short s = (short) (MAX_16_BIT * sample);
buffer[bufferSize++] = (byte) s;
buffer[bufferSize++] = (byte) (s >> 8); // little Endian
// send to sound card if buffer is full
if (bufferSize >= buffer.length) {
line.write(buffer, 0, buffer.length);
bufferSize = 0;
}
}
/**
* Writes the array of samples (between -1.0 and +1.0) to standard audio.
* If a sample is outside the range, it will be clipped.
*
* @param samples the array of samples to play
* @throws IllegalArgumentException if any sample is {@code Double.NaN}
* @throws IllegalArgumentException if {@code samples} is {@code null}
*/
public static void play(double[] samples) {
if (samples == null) throw new IllegalArgumentException("argument to play() is null");
for (int i = 0; i < samples.length; i++) {
play(samples[i]);
}
}
/**
* Reads audio samples from a file (in .wav or .au format) and returns
* them as a double array with values between -1.0 and +1.0.
*
* @param filename the name of the audio file
* @return the array of samples
*/
public static double[] read(String filename) {
byte[] data = readByte(filename);
int n = data.length;
double[] d = new double[n/2];
for (int i = 0; i < n/2; i++) {
d[i] = ((short) (((data[2*i+1] & 0xFF) << 8) + (data[2*i] & 0xFF))) / ((double) MAX_16_BIT);
}
return d;
}
// return data as a byte array
private static byte[] readByte(String filename) {
byte[] data = null;
AudioInputStream ais = null;
try {
// try to read from file
File file = new File(filename);
if (file.exists()) {
ais = AudioSystem.getAudioInputStream(file);
int bytesToRead = ais.available();
data = new byte[bytesToRead];
int bytesRead = ais.read(data);
if (bytesToRead != bytesRead)
throw new IllegalStateException("read only " + bytesRead + " of " + bytesToRead + " bytes");
}
// try to read from URL
else {
URL url = StdAudio.class.getResource(filename);
ais = AudioSystem.getAudioInputStream(url);
int bytesToRead = ais.available();
data = new byte[bytesToRead];
int bytesRead = ais.read(data);
if (bytesToRead != bytesRead)
throw new IllegalStateException("read only " + bytesRead + " of " + bytesToRead + " bytes");
}
}
catch (IOException e) {
throw new IllegalArgumentException("could not read '" + filename + "'", e);
}
catch (UnsupportedAudioFileException e) {
throw new IllegalArgumentException("unsupported audio format: '" + filename + "'", e);
}
return data;
}
/**
* Saves the double array as an audio file (using .wav or .au format).
*
* @param filename the name of the audio file
* @param samples the array of samples
* @throws IllegalArgumentException if unable to save {@code filename}
* @throws IllegalArgumentException if {@code samples} is {@code null}
*/
public static void save(String filename, double[] samples) {
if (samples == null) {
throw new IllegalArgumentException("samples[] is null");
}
// assumes 44,100 samples per second
// use 16-bit audio, mono, signed PCM, little Endian
AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, 1, true, false);
byte[] data = new byte[2 * samples.length];
for (int i = 0; i < samples.length; i++) {
int temp = (short) (samples[i] * MAX_16_BIT);
data[2*i + 0] = (byte) temp;
data[2*i + 1] = (byte) (temp >> 8);
}
// now save the file
try {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
AudioInputStream ais = new AudioInputStream(bais, format, samples.length);
if (filename.endsWith(".wav") || filename.endsWith(".WAV")) {
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File(filename));
}
else if (filename.endsWith(".au") || filename.endsWith(".AU")) {
AudioSystem.write(ais, AudioFileFormat.Type.AU, new File(filename));
}
else {
throw new IllegalArgumentException("unsupported audio format: '" + filename + "'");
}
}
catch (IOException ioe) {
throw new IllegalArgumentException("unable to save file '" + filename + "'", ioe);
}
}
/**
* Plays an audio file (in .wav, .mid, or .au format) in a background thread.
*
* @param filename the name of the audio file
* @throws IllegalArgumentException if unable to play {@code filename}
* @throws IllegalArgumentException if {@code filename} is {@code null}
*/
public static synchronized void play(final String filename) {
if (filename == null) throw new IllegalArgumentException();
InputStream is = StdAudio.class.getResourceAsStream(filename);
if (is == null) {
throw new IllegalArgumentException("could not read '" + filename + "'");
}
// code adapted from: http://stackoverflow.com/questions/26305/how-can-i-play-sound-in-java
try {
// check if file format is supported
// (if not, will throw an UnsupportedAudioFileException)
AudioSystem.getAudioInputStream(is);
new Thread(new Runnable() {
@Override
public void run() {
stream(filename);
}
}).start();
}
// let's try Applet.newAudioClip() instead
catch (UnsupportedAudioFileException e) {
playApplet(filename);
return;
}
// something else went wrong
catch (IOException ioe) {
throw new IllegalArgumentException("could not play '" + filename + "'", ioe);
}
}
// play sound file using Applet.newAudioClip();
@SuppressWarnings("deprecation")
private static void playApplet(String filename) {
URL url = null;
try {
File file = new File(filename);
if (file.canRead()) url = file.toURI().toURL();
}
catch (MalformedURLException e) {
throw new IllegalArgumentException("could not play '" + filename + "'", e);
}
// URL url = StdAudio.class.getResource(filename);
if (url == null) {
throw new IllegalArgumentException("could not play '" + filename + "'");
}
AudioClip clip = Applet.newAudioClip(url);
clip.play();
}
// https://www3.ntu.edu.sg/home/ehchua/programming/java/J8c_PlayingSound.html
// play a wav or aif file
// javax.sound.sampled.Clip fails for long clips (on some systems)
private static void stream(String filename) {
SourceDataLine line = null;
int BUFFER_SIZE = 4096; // 4K buffer
try {
InputStream is = StdAudio.class.getResourceAsStream(filename);
AudioInputStream ais = AudioSystem.getAudioInputStream(is);
AudioFormat audioFormat = ais.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
line.start();
byte[] samples = new byte[BUFFER_SIZE];
int count = 0;
while ((count = ais.read(samples, 0, BUFFER_SIZE)) != -1) {
line.write(samples, 0, count);
}
}
catch (IOException e) {
e.printStackTrace();
}
catch (UnsupportedAudioFileException e) {
e.printStackTrace();
}
catch (LineUnavailableException e) {
e.printStackTrace();
}
finally {
if (line != null) {
line.drain();
line.close();
}
}
}
/**
* Loops an audio file (in .wav, .mid, or .au format) in a background thread.
*
* @param filename the name of the audio file
* @throws IllegalArgumentException if {@code filename} is {@code null}
*/
public static synchronized void loop(String filename) {
if (filename == null) throw new IllegalArgumentException();
// code adapted from: http://stackoverflow.com/questions/26305/how-can-i-play-sound-in-java
try {
Clip clip = AudioSystem.getClip();
InputStream is = StdAudio.class.getResourceAsStream(filename);
AudioInputStream ais = AudioSystem.getAudioInputStream(is);
clip.open(ais);
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
catch (UnsupportedAudioFileException e) {
throw new IllegalArgumentException("unsupported audio format: '" + filename + "'", e);
}
catch (LineUnavailableException e) {
throw new IllegalArgumentException("could not play '" + filename + "'", e);
}
catch (IOException e) {
throw new IllegalArgumentException("could not play '" + filename + "'", e);
}
}
/***************************************************************************
* Unit tests {@code StdAudio}.
***************************************************************************/
// create a note (sine wave) of the given frequency (Hz), for the given
// duration (seconds) scaled to the given volume (amplitude)
private static double[] note(double hz, double duration, double amplitude) {
int n = (int) (StdAudio.SAMPLE_RATE * duration);
double[] a = new double[n+1];
for (int i = 0; i <= n; i++)
a[i] = amplitude * Math.sin(2 * Math.PI * i * hz / StdAudio.SAMPLE_RATE);
return a;
}
/**
* Test client - play an A major scale to standard audio.
*
* @param args the command-line arguments
*/
/**
* Test client - play an A major scale to standard audio.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
// 440 Hz for 1 sec
double freq = 440.0;
for (int i = 0; i <= StdAudio.SAMPLE_RATE; i++) {
StdAudio.play(0.5 * Math.sin(2*Math.PI * freq * i / StdAudio.SAMPLE_RATE));
}
// scale increments
int[] steps = { 0, 2, 4, 5, 7, 9, 11, 12 };
for (int i = 0; i < steps.length; i++) {
double hz = 440.0 * Math.pow(2, steps[i] / 12.0);
StdAudio.play(note(hz, 1.0, 0.5));
}
// need to call this in non-interactive stuff so the program doesn't terminate
// until all the sound leaves the speaker.
StdAudio.close();
}
}
package edu.caltech.cs2.project03.libraries; /******************************************************************************
* Compilation: javac StdDraw.java
* Execution: java StdDraw
* Dependencies: none
*
* Standard drawing library. This class provides a basic capability for
* creating drawings with your programs. It uses a simple graphics model that
* allows you to create drawings consisting of points, lines, and curves
* in a window on your computer and to save the drawings to a file.
*
* Todo
* ----
* - Add support for gradient fill, etc.
* - Fix setCanvasSize() so that it can only be called once.
* - On some systems, drawing a line (or other shape) that extends way
* beyond canvas (e.g., to infinity) dimensions does not get drawn.
*
* Remarks
* -------
* - don't use AffineTransform for rescaling since it inverts
* images and strings
*
******************************************************************************/
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.awt.image.DirectColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.TreeSet;
/**
* The {@code StdDraw} class provides a basic capability for
* creating drawings with your programs. It uses a simple graphics model that
* allows you to create drawings consisting of points, lines, squares,
* circles, and other geometric shapes in a window on your computer and
* to save the drawings to a file. Standard drawing also includes
* facilities for text, color, pictures, and animation, along with
* user interaction via the keyboard and mouse.
* <p>
* <b>Getting started.</b>
* To use this class, you must have {@code StdDraw.class} in your
* Java classpath. If you used our autoinstaller, you should be all set.
* Otherwise, either download
* <a href = "https://introcs.cs.princeton.edu/java/code/stdlib.jar">stdlib.jar</a>
* and add to your Java classpath or download
* <a href = "https://introcs.cs.princeton.edu/java/stdlib/StdDraw.java">StdDraw.java</a>
* and put a copy in your working directory.
* <p>
* Now, type the following short program into your editor:
* <pre>
* public class TestStdDraw {
* public static void main(String[] args) {
* StdDraw.setPenRadius(0.05);
* StdDraw.setPenColor(StdDraw.BLUE);
* StdDraw.point(0.5, 0.5);
* StdDraw.setPenColor(StdDraw.MAGENTA);
* StdDraw.line(0.2, 0.2, 0.8, 0.2);
* }
* }
* </pre>
* If you compile and execute the program, you should see a window
* appear with a thick magenta line and a blue point.
* This program illustrates the two main types of methods in standard
* drawing—methods that draw geometric shapes and methods that
* control drawing parameters.
* The methods {@code StdDraw.line()} and {@code StdDraw.point()}
* draw lines and points; the methods {@code StdDraw.setPenRadius()}
* and {@code StdDraw.setPenColor()} control the line thickness and color.
* <p>
* <b>Points and lines.</b>
* You can draw points and line segments with the following methods:
* <ul>
* <li> {@link #point(double x, double y)}
* <li> {@link #line(double x1, double y1, double x2, double y2)}
* </ul>
* <p>
* The <em>x</em>- and <em>y</em>-coordinates must be in the drawing area
* (between 0 and 1 and by default) or the points and lines will not be visible.
* <p>
* <b>Squares, circles, rectangles, and ellipses.</b>
* You can draw squares, circles, rectangles, and ellipses using
* the following methods:
* <ul>
* <li> {@link #circle(double x, double y, double radius)}
* <li> {@link #ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis)}
* <li> {@link #square(double x, double y, double radius)}
* <li> {@link #rectangle(double x, double y, double halfWidth, double halfHeight)}
* </ul>
* <p>
* All of these methods take as arguments the location and size of the shape.
* The location is always specified by the <em>x</em>- and <em>y</em>-coordinates
* of its <em>center</em>.
* The size of a circle is specified by its radius and the size of an ellipse is
* specified by the lengths of its semi-major and semi-minor axes.
* The size of a square or rectangle is specified by its half-width or half-height.
* The convention for drawing squares and rectangles is parallel to those for
* drawing circles and ellipses, but may be unexpected to the uninitiated.
* <p>
* The methods above trace outlines of the given shapes. The following methods
* draw filled versions:
* <ul>
* <li> {@link #filledCircle(double x, double y, double radius)}
* <li> {@link #filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis)}
* <li> {@link #filledSquare(double x, double y, double radius)}
* <li> {@link #filledRectangle(double x, double y, double halfWidth, double halfHeight)}
* </ul>
* <p>
* <b>Circular arcs.</b>
* You can draw circular arcs with the following method:
* <ul>
* <li> {@link #arc(double x, double y, double radius, double angle1, double angle2)}
* </ul>
* <p>
* The arc is from the circle centered at (<em>x</em>, <em>y</em>) of the specified radius.
* The arc extends from angle1 to angle2. By convention, the angles are
* <em>polar</em> (counterclockwise angle from the <em>x</em>-axis)
* and represented in degrees. For example, {@code StdDraw.arc(0.0, 0.0, 1.0, 0, 90)}
* draws the arc of the unit circle from 3 o'clock (0 degrees) to 12 o'clock (90 degrees).
* <p>
* <b>Polygons.</b>
* You can draw polygons with the following methods:
* <ul>
* <li> {@link #polygon(double[] x, double[] y)}
* <li> {@link #filledPolygon(double[] x, double[] y)}
* </ul>
* <p>
* The points in the polygon are ({@code x[i]}, {@code y[i]}).
* For example, the following code fragment draws a filled diamond
* with vertices (0.1, 0.2), (0.2, 0.3), (0.3, 0.2), and (0.2, 0.1):
* <pre>
* double[] x = { 0.1, 0.2, 0.3, 0.2 };
* double[] y = { 0.2, 0.3, 0.2, 0.1 };
* StdDraw.filledPolygon(x, y);
* </pre>
* <p>
* <b>Pen size.</b>
* The pen is circular, so that when you set the pen radius to <em>r</em>
* and draw a point, you get a circle of radius <em>r</em>. Also, lines are
* of thickness 2<em>r</em> and have rounded ends. The default pen radius
* is 0.005 and is not affected by coordinate scaling. This default pen
* radius is about 1/200 the width of the default canvas, so that if
* you draw 100 points equally spaced along a horizontal or vertical line,
* you will be able to see individual circles, but if you draw 200 such
* points, the result will look like a line.
* <ul>
* <li> {@link #setPenRadius(double radius)}
* </ul>
* <p>
* For example, {@code StdDraw.setPenRadius(0.025)} makes
* the thickness of the lines and the size of the points to be five times
* the 0.005 default.
* To draw points with the minimum possible radius (one pixel on typical
* displays), set the pen radius to 0.0.
* <p>
* <b>Pen color.</b>
* All geometric shapes (such as points, lines, and circles) are drawn using
* the current pen color. By default, it is black.
* You can change the pen color with the following methods:
* <ul>
* <li> {@link #setPenColor(int red, int green, int blue)}
* <li> {@link #setPenColor(Color color)}
* </ul>
* <p>
* The first method allows you to specify colors using the RGB color system.
* This <a href = "http://johndyer.name/lab/colorpicker/">color picker</a>
* is a convenient way to find a desired color.
* The second method allows you to specify colors using the
* {@link Color} data type that is discussed in Chapter 3. Until then,
* you can use this method with one of these predefined colors in standard drawing:
* {@link #BLACK}, {@link #BLUE}, {@link #CYAN}, {@link #DARK_GRAY}, {@link #GRAY},
* {@link #GREEN}, {@link #LIGHT_GRAY}, {@link #MAGENTA}, {@link #ORANGE},
* {@link #PINK}, {@link #RED}, {@link #WHITE}, {@link #YELLOW},
* {@link #BOOK_BLUE}, {@link #BOOK_LIGHT_BLUE}, {@link #BOOK_RED}, and
* {@link #PRINCETON_ORANGE}.
* For example, {@code StdDraw.setPenColor(StdDraw.MAGENTA)} sets the
* pen color to magenta.
* <p>
* <b>Canvas size.</b>
* By default, all drawing takes places in a 512-by-512 canvas.
* The canvas does not include the window title or window border.
* You can change the size of the canvas with the following method:
* <ul>
* <li> {@link #setCanvasSize(int width, int height)}
* </ul>
* <p>
* This sets the canvas size to be <em>width</em>-by-<em>height</em> pixels.
* It also erases the current drawing and resets the coordinate system,
* pen radius, pen color, and font back to their default values.
* Ordinarly, this method is called once, at the very beginning of a program.
* For example, {@code StdDraw.setCanvasSize(800, 800)}
* sets the canvas size to be 800-by-800 pixels.
* <p>
* <b>Canvas scale and coordinate system.</b>
* By default, all drawing takes places in the unit square, with (0, 0) at
* lower left and (1, 1) at upper right. You can change the default
* coordinate system with the following methods:
* <ul>
* <li> {@link #setXscale(double xmin, double xmax)}
* <li> {@link #setYscale(double ymin, double ymax)}
* <li> {@link #setScale(double min, double max)}
* </ul>
* <p>
* The arguments are the coordinates of the minimum and maximum
* <em>x</em>- or <em>y</em>-coordinates that will appear in the canvas.
* For example, if you wish to use the default coordinate system but
* leave a small margin, you can call {@code StdDraw.setScale(-.05, 1.05)}.
* <p>
* These methods change the coordinate system for subsequent drawing
* commands; they do not affect previous drawings.
* These methods do not change the canvas size; so, if the <em>x</em>-
* and <em>y</em>-scales are different, squares will become rectangles
* and circles will become ellipsoidal.
* <p>
* <b>Text.</b>
* You can use the following methods to annotate your drawings with text:
* <ul>
* <li> {@link #text(double x, double y, String text)}
* <li> {@link #text(double x, double y, String text, double degrees)}
* <li> {@link #textLeft(double x, double y, String text)}
* <li> {@link #textRight(double x, double y, String text)}
* </ul>
* <p>
* The first two methods write the specified text in the current font,
* centered at (<em>x</em>, <em>y</em>).
* The second method allows you to rotate the text.
* The last two methods either left- or right-align the text at (<em>x</em>, <em>y</em>).
* <p>
* The default font is a Sans Serif font with point size 16.
* You can use the following method to change the font:
* <ul>
* <li> {@link #setFont(Font font)}
* </ul>
* <p>
* You use the {@link Font} data type to specify the font. This allows you to
* choose the face, size, and style of the font. For example, the following
* code fragment sets the font to Arial Bold, 60 point.
* <pre>
* Font font = new Font("Arial", Font.BOLD, 60);
* StdDraw.setFont(font);
* StdDraw.text(0.5, 0.5, "Hello, World");
* </pre>
* <p>
* <b>Images.</b>
* You can use the following methods to add images to your drawings:
* <ul>
* <li> {@link #picture(double x, double y, String filename)}
* <li> {@link #picture(double x, double y, String filename, double degrees)}
* <li> {@link #picture(double x, double y, String filename, double scaledWidth, double scaledHeight)}
* <li> {@link #picture(double x, double y, String filename, double scaledWidth, double scaledHeight, double degrees)}
* </ul>
* <p>
* These methods draw the specified image, centered at (<em>x</em>, <em>y</em>).
* The supported image formats are JPEG, PNG, and GIF.
* The image will display at its native size, independent of the coordinate system.
* Optionally, you can rotate the image a specified number of degrees counterclockwise
* or rescale it to fit snugly inside a width-by-height bounding box.
* <p>
* <b>Saving to a file.</b>
* You save your image to a file using the <em>File → Save</em> menu option.
* You can also save a file programatically using the following method:
* <ul>
* <li> {@link #save(String filename)}
* </ul>
* <p>
* The supported image formats are JPEG and PNG. The filename must have either the
* extension .jpg or .png.
* We recommend using PNG for drawing that consist solely of geometric shapes and JPEG
* for drawings that contains pictures.
* <p>
* <b>Clearing the canvas.</b>
* To clear the entire drawing canvas, you can use the following methods:
* <ul>
* <li> {@link #clear()}
* <li> {@link #clear(Color color)}
* </ul>
* <p>
* The first method clears the canvas to white; the second method
* allows you to specify a color of your choice. For example,
* {@code StdDraw.clear(StdDraw.LIGHT_GRAY)} clears the canvas to a shade
* of gray.
* <p>
* <b>Computer animations and double buffering.</b>
* Double buffering is one of the most powerful features of standard drawing,
* enabling computer animations.
* The following methods control the way in which objects are drawn:
* <ul>
* <li> {@link #enableDoubleBuffering()}
* <li> {@link #disableDoubleBuffering()}
* <li> {@link #show()}
* <li> {@link #pause(int t)}
* </ul>
* <p>
* By default, double buffering is disabled, which means that as soon as you
* call a drawing
* method—such as {@code point()} or {@code line()}—the
* results appear on the screen.
* <p>
* When double buffering is enabled by calling {@link #enableDoubleBuffering()},
* all drawing takes place on the <em>offscreen canvas</em>. The offscreen canvas
* is not displayed. Only when you call
* {@link #show()} does your drawing get copied from the offscreen canvas to
* the onscreen canvas, where it is displayed in the standard drawing window. You
* can think of double buffering as collecting all of the lines, points, shapes,
* and text that you tell it to draw, and then drawing them all
* <em>simultaneously</em>, upon request.
* <p>
* The most important use of double buffering is to produce computer
* animations, creating the illusion of motion by rapidly
* displaying static drawings. To produce an animation, repeat
* the following four steps:
* <ul>
* <li> Clear the offscreen canvas.
* <li> Draw objects on the offscreen canvas.
* <li> Copy the offscreen canvas to the onscreen canvas.
* <li> Wait for a short while.
* </ul>
* <p>
* The {@link #clear()}, {@link #show()}, and {@link #pause(int t)} methods
* support the first, third, and fourth of these steps, respectively.
* <p>
* For example, this code fragment animates two balls moving in a circle.
* <pre>
* StdDraw.setScale(-2, +2);
* StdDraw.enableDoubleBuffering();
*
* for (double t = 0.0; true; t += 0.02) {
* double x = Math.sin(t);
* double y = Math.cos(t);
* StdDraw.clear();
* StdDraw.filledCircle(x, y, 0.05);
* StdDraw.filledCircle(-x, -y, 0.05);
* StdDraw.show();
* StdDraw.pause(20);
* }
* </pre>
* <p>
* <b>Keyboard and mouse inputs.</b>
* Standard drawing has very basic support for keyboard and mouse input.
* It is much less powerful than most user interface libraries provide, but also much simpler.
* You can use the following methods to intercept mouse events:
* <ul>
* <li> {@link #isMousePressed()}
* <li> {@link #mouseX()}
* <li> {@link #mouseY()}
* </ul>
* <p>
* The first method tells you whether a mouse button is currently being pressed.
* The last two methods tells you the <em>x</em>- and <em>y</em>-coordinates of the mouse's
* current position, using the same coordinate system as the canvas (the unit square, by default).
* You should use these methods in an animation loop that waits a short while before trying
* to poll the mouse for its current state.
* You can use the following methods to intercept keyboard events:
* <ul>
* <li> {@link #hasNextKeyTyped()}
* <li> {@link #nextKeyTyped()}
* <li> {@link #isKeyPressed(int keycode)}
* </ul>
* <p>
* If the user types lots of keys, they will be saved in a list until you process them.
* The first method tells you whether the user has typed a key (that your program has
* not yet processed).
* The second method returns the next key that the user typed (that your program has
* not yet processed) and removes it from the list of saved keystrokes.
* The third method tells you whether a key is currently being pressed.
* <p>
* <b>Accessing control parameters.</b>
* You can use the following methods to access the current pen color, pen radius,
* and font:
* <ul>
* <li> {@link #getPenColor()}
* <li> {@link #getPenRadius()}
* <li> {@link #getFont()}
* </ul>
* <p>
* These methods are useful when you want to temporarily change a
* control parameter and reset it back to its original value.
* <p>
* <b>Corner cases.</b>
* To avoid clutter, the API doesn't explicitly refer to arguments that are
* null, infinity, or NaN.
* <ul>
* <li> Any method that is passed a {@code null} argument will throw an
* {@link IllegalArgumentException}.
* <li> Except as noted in the APIs, drawing an object outside (or partly outside)
* the canvas is permitted—however, only the part of the object that
* appears inside the canvas will be visible.
* <li> Except as noted in the APIs, all methods accept {@link Double#NaN},
* {@link Double#POSITIVE_INFINITY}, and {@link Double#NEGATIVE_INFINITY}
* as arugments. An object drawn with an <em>x</em>- or <em>y</em>-coordinate
* that is NaN will behave as if it is outside the canvas, and will not be visible.
* <li> Due to floating-point issues, an object drawn with an <em>x</em>- or
* <em>y</em>-coordinate that is way outside the canvas (such as the line segment
* from (0.5, –&infin;) to (0.5, &infin;) may not be visible even in the
* part of the canvas where it should be.
* </ul>
* <p>
* <b>Performance tricks.</b>
* Standard drawing is capable of drawing large amounts of data.
* Here are a few tricks and tips:
* <ul>
* <li> Use <em>double buffering</em> for static drawing with a large
* number of objects.
* That is, call {@link #enableDoubleBuffering()} before
* the sequence of drawing commands and call {@link #show()} afterwards.
* Incrementally displaying a complex drawing while it is being
* created can be intolerably inefficient on many computer systems.
* <li> When drawing computer animations, call {@code show()}
* only once per frame, not after drawing each individual object.
* <li> If you call {@code picture()} multiple times with the same filename,
* Java will cache the image, so you do not incur the cost of reading
* from a file each time.
* </ul>
* <p>
* <b>Known bugs and issues.</b>
* <ul>
* <li> The {@code picture()} methods may not draw the portion of the image that is
* inside the canvas if the center point (<em>x</em>, <em>y</em>) is outside the
* canvas.
* This bug appears only on some systems.
* <li> Some methods may not draw the portion of the geometric object that is inside the
* canvas if the <em>x</em>- or <em>y</em>-coordinates are infinite.
* This bug appears only on some systems.
* </ul>
* <p>
* <b>Reference.</b>
* For additional documentation,
* see <a href="https://introcs.cs.princeton.edu/15inout">Section 1.5</a> of
* <em>Computer Science: An Interdisciplinary Approach</em>
* by Robert Sedgewick and Kevin Wayne.
*
* @author Robert Sedgewick
* @author Kevin Wayne
*/
public final class StdDraw implements ActionListener, MouseListener, MouseMotionListener, KeyListener {
private static boolean DO_NOTHING = false;
private static boolean NO_PAUSE = false;
/**
* The color black.
*/
public static final Color BLACK = Color.BLACK;
/**
* The color blue.
*/
public static final Color BLUE = Color.BLUE;
/**
* The color cyan.
*/
public static final Color CYAN = Color.CYAN;
/**
* The color dark gray.
*/
public static final Color DARK_GRAY = Color.DARK_GRAY;
/**
* The color gray.
*/
public static final Color GRAY = Color.GRAY;
/**
* The color green.
*/
public static final Color GREEN = Color.GREEN;
/**
* The color light gray.
*/
public static final Color LIGHT_GRAY = Color.LIGHT_GRAY;
/**
* The color magenta.
*/
public static final Color MAGENTA = Color.MAGENTA;
/**
* The color orange.
*/
public static final Color ORANGE = Color.ORANGE;
/**
* The color pink.
*/
public static final Color PINK = Color.PINK;
/**
* The color red.
*/
public static final Color RED = Color.RED;
/**
* The color white.
*/
public static final Color WHITE = Color.WHITE;
/**
* The color yellow.
*/
public static final Color YELLOW = Color.YELLOW;
/**
* Shade of blue used in <em>Introduction to Programming in Java</em>.
* It is Pantone 300U. The RGB values are approximately (9, 90, 166).
*/
public static final Color BOOK_BLUE = new Color(9, 90, 166);
/**
* Shade of light blue used in <em>Introduction to Programming in Java</em>.
* The RGB values are approximately (103, 198, 243).
*/
public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243);
/**
* Shade of red used in <em>Algorithms, 4th edition</em>.
* It is Pantone 1805U. The RGB values are approximately (150, 35, 31).
*/
public static final Color BOOK_RED = new Color(150, 35, 31);
/**
* Shade of orange used in Princeton University's identity.
* It is PMS 158. The RGB values are approximately (245, 128, 37).
*/
public static final Color PRINCETON_ORANGE = new Color(245, 128, 37);
// default colors
private static final Color DEFAULT_PEN_COLOR = BLACK;
private static final Color DEFAULT_CLEAR_COLOR = WHITE;
// current pen color
private static Color penColor;
// default canvas size is DEFAULT_SIZE-by-DEFAULT_SIZE
private static final int DEFAULT_SIZE = 512;
private static int width = DEFAULT_SIZE;
private static int height = DEFAULT_SIZE;
// default pen radius
private static final double DEFAULT_PEN_RADIUS = 0.002;
// current pen radius
private static double penRadius;
// show we draw immediately or wait until next show?
private static boolean defer = false;
// boundary of drawing canvas, 0% border
// private static final double BORDER = 0.05;
private static final double BORDER = 0.00;
private static final double DEFAULT_XMIN = 0.0;
private static final double DEFAULT_XMAX = 1.0;
private static final double DEFAULT_YMIN = 0.0;
private static final double DEFAULT_YMAX = 1.0;
private static double xmin, ymin, xmax, ymax;
// for synchronization
private static Object mouseLock = new Object();
private static Object keyLock = new Object();
// default font
private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16);
// current font
private static Font font;
// double buffered graphics
private static BufferedImage offscreenImage, onscreenImage;
private static Graphics2D offscreen, onscreen;
// singleton for callbacks: avoids generation of extra .class files
private static StdDraw std = new StdDraw();
// the frame for drawing to the screen
private static JFrame frame;
// mouse state
private static boolean isMousePressed = false;
private static double mouseX = 0;
private static double mouseY = 0;
// queue of typed key characters
private static LinkedList<Character> keysTyped = new LinkedList<Character>();
// set of key codes currently pressed down
private static TreeSet<Integer> keysDown = new TreeSet<Integer>();
// singleton pattern: client can't instantiate
private StdDraw() { }
public static void setDoNothing(boolean b) {
DO_NOTHING = b;
frame.setVisible(false);
}
public static void setNoPause(boolean b) {
NO_PAUSE = b;
frame.setVisible(false);
}
public static void showFrame(boolean b) {
frame.setVisible(b);
}
// static initializer
static {
if (!DO_NOTHING) {
init();
}
}
/**
* Sets the canvas (drawing area) to be 512-by-512 pixels.
* This also erases the current drawing and resets the coordinate system,
* pen radius, pen color, and font back to their default values.
* Ordinarly, this method is called once, at the very beginning
* of a program.
*/
public static void setCanvasSize() {
setCanvasSize(DEFAULT_SIZE, DEFAULT_SIZE);
}
/**
* Sets the canvas (drawing area) to be <em>width</em>-by-<em>height</em> pixels.
* This also erases the current drawing and resets the coordinate system,
* pen radius, pen color, and font back to their default values.
* Ordinarly, this method is called once, at the very beginning
* of a program.
*
* @param canvasWidth the width as a number of pixels
* @param canvasHeight the height as a number of pixels
* @throws IllegalArgumentException unless both {@code canvasWidth} and
* {@code canvasHeight} are positive
*/
public static void setCanvasSize(int canvasWidth, int canvasHeight) {
if (canvasWidth <= 0 || canvasHeight <= 0)
throw new IllegalArgumentException("width and height must be positive");
width = canvasWidth;
height = canvasHeight;
init();
}
// init
private static void init() {
if (DO_NOTHING) {
return;
}
if (frame != null) frame.setVisible(false);
frame = new JFrame();
offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
onscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
offscreen = offscreenImage.createGraphics();
onscreen = onscreenImage.createGraphics();
setXscale();
setYscale();
offscreen.setColor(DEFAULT_CLEAR_COLOR);
offscreen.fillRect(0, 0, width, height);
setPenColor();
setPenRadius();
setFont();
clear();
// add antialiasing
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
offscreen.addRenderingHints(hints);
// frame stuff
ImageIcon icon = new ImageIcon(onscreenImage);
JLabel draw = new JLabel(icon);
draw.addMouseListener(std);
draw.addMouseMotionListener(std);
frame.setContentPane(draw);
frame.addKeyListener(std); // JLabel cannot get keyboard focus
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows
// frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // closes only current window
frame.setTitle("Standard Draw");
frame.setJMenuBar(createMenuBar());
frame.pack();
frame.requestFocusInWindow();
frame.setVisible(true);
}
// create the menu bar (changed to private)
@SuppressWarnings("deprecation")
private static JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem menuItem1 = new JMenuItem(" Save... ");
menuItem1.addActionListener(std);
menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
menu.add(menuItem1);
return menuBar;
}
/***************************************************************************
* User and screen coordinate systems.
***************************************************************************/
/**
* Sets the <em>x</em>-scale to be the default (between 0.0 and 1.0).
*/
public static void setXscale() {
setXscale(DEFAULT_XMIN, DEFAULT_XMAX);
}
/**
* Sets the <em>y</em>-scale to be the default (between 0.0 and 1.0).
*/
public static void setYscale() {
setYscale(DEFAULT_YMIN, DEFAULT_YMAX);
}
/**
* Sets the <em>x</em>-scale and <em>y</em>-scale to be the default
* (between 0.0 and 1.0).
*/
public static void setScale() {
setXscale();
setYscale();
}
/**
* Sets the <em>x</em>-scale to the specified range.
*
* @param min the minimum value of the <em>x</em>-scale
* @param max the maximum value of the <em>x</em>-scale
* @throws IllegalArgumentException if {@code (max == min)}
*/
public static void setXscale(double min, double max) {
double size = max - min;
if (size == 0.0) throw new IllegalArgumentException("the min and max are the same");
synchronized (mouseLock) {
xmin = min - BORDER * size;
xmax = max + BORDER * size;
}
}
/**
* Sets the <em>y</em>-scale to the specified range.
*
* @param min the minimum value of the <em>y</em>-scale
* @param max the maximum value of the <em>y</em>-scale
* @throws IllegalArgumentException if {@code (max == min)}
*/
public static void setYscale(double min, double max) {
double size = max - min;
if (size == 0.0) throw new IllegalArgumentException("the min and max are the same");
synchronized (mouseLock) {
ymin = min - BORDER * size;
ymax = max + BORDER * size;
}
}
/**
* Sets both the <em>x</em>-scale and <em>y</em>-scale to the (same) specified range.
*
* @param min the minimum value of the <em>x</em>- and <em>y</em>-scales
* @param max the maximum value of the <em>x</em>- and <em>y</em>-scales
* @throws IllegalArgumentException if {@code (max == min)}
*/
public static void setScale(double min, double max) {
if (DO_NOTHING) {
return;
}
double size = max - min;
if (size == 0.0) throw new IllegalArgumentException("the min and max are the same");
synchronized (mouseLock) {
xmin = min - BORDER * size;
xmax = max + BORDER * size;
ymin = min - BORDER * size;
ymax = max + BORDER * size;
}
}
// helper functions that scale from user coordinates to screen coordinates and back
private static double scaleX(double x) { return width * (x - xmin) / (xmax - xmin); }
private static double scaleY(double y) { return height * (ymax - y) / (ymax - ymin); }
private static double factorX(double w) { return w * width / Math.abs(xmax - xmin); }
private static double factorY(double h) { return h * height / Math.abs(ymax - ymin); }
private static double userX(double x) { return xmin + x * (xmax - xmin) / width; }
private static double userY(double y) { return ymax - y * (ymax - ymin) / height; }
/**
* Clears the screen to the default color (white).
*/
public static void clear() {
clear(DEFAULT_CLEAR_COLOR);
}
/**
* Clears the screen to the specified color.
*
* @param color the color to make the background
*/
public static void clear(Color color) {
offscreen.setColor(color);
offscreen.fillRect(0, 0, width, height);
offscreen.setColor(penColor);
draw();
}
/**
* Returns the current pen radius.
*
* @return the current value of the pen radius
*/
public static double getPenRadius() {
return penRadius;
}
/**
* Sets the pen size to the default size (0.002).
* The pen is circular, so that lines have rounded ends, and when you set the
* pen radius and draw a point, you get a circle of the specified radius.
* The pen radius is not affected by coordinate scaling.
*/
public static void setPenRadius() {
setPenRadius(DEFAULT_PEN_RADIUS);
}
/**
* Sets the radius of the pen to the specified size.
* The pen is circular, so that lines have rounded ends, and when you set the
* pen radius and draw a point, you get a circle of the specified radius.
* The pen radius is not affected by coordinate scaling.
*
* @param radius the radius of the pen
* @throws IllegalArgumentException if {@code radius} is negative
*/
public static void setPenRadius(double radius) {
if (!(radius >= 0)) throw new IllegalArgumentException("pen radius must be nonnegative");
penRadius = radius;
float scaledPenRadius = (float) (radius * DEFAULT_SIZE);
BasicStroke stroke = new BasicStroke(scaledPenRadius, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
// BasicStroke stroke = new BasicStroke(scaledPenRadius);
offscreen.setStroke(stroke);
}
/**
* Returns the current pen color.
*
* @return the current pen color
*/
public static Color getPenColor() {
return penColor;
}
/**
* Set the pen color to the default color (black).
*/
public static void setPenColor() {
setPenColor(DEFAULT_PEN_COLOR);
}
/**
* Sets the pen color to the specified color.
* <p>
* The predefined pen colors are
* {@code StdDraw.BLACK}, {@code StdDraw.BLUE}, {@code StdDraw.CYAN},
* {@code StdDraw.DARK_GRAY}, {@code StdDraw.GRAY}, {@code StdDraw.GREEN},
* {@code StdDraw.LIGHT_GRAY}, {@code StdDraw.MAGENTA}, {@code StdDraw.ORANGE},
* {@code StdDraw.PINK}, {@code StdDraw.RED}, {@code StdDraw.WHITE}, and
* {@code StdDraw.YELLOW}.
*
* @param color the color to make the pen
*/
public static void setPenColor(Color color) {
if (color == null) throw new IllegalArgumentException();
penColor = color;
offscreen.setColor(penColor);
}
/**
* Sets the pen color to the specified RGB color.
*
* @param red the amount of red (between 0 and 255)
* @param green the amount of green (between 0 and 255)
* @param blue the amount of blue (between 0 and 255)
* @throws IllegalArgumentException if {@code red}, {@code green},
* or {@code blue} is outside its prescribed range
*/
public static void setPenColor(int red, int green, int blue) {
if (red < 0 || red >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255");
if (green < 0 || green >= 256) throw new IllegalArgumentException("amount of green must be between 0 and 255");
if (blue < 0 || blue >= 256) throw new IllegalArgumentException("amount of blue must be between 0 and 255");
setPenColor(new Color(red, green, blue));
}
/**
* Returns the current font.
*
* @return the current font
*/
public static Font getFont() {
return font;
}
/**
* Sets the font to the default font (sans serif, 16 point).
*/
public static void setFont() {
setFont(DEFAULT_FONT);
}
/**
* Sets the font to the specified value.
*
* @param font the font
*/
public static void setFont(Font font) {
if (font == null) throw new IllegalArgumentException();
StdDraw.font = font;
}
/***************************************************************************
* Drawing geometric shapes.
***************************************************************************/
/**
* Draws a line segment between (<em>x</em><sub>0</sub>, <em>y</em><sub>0</sub>) and
* (<em>x</em><sub>1</sub>, <em>y</em><sub>1</sub>).
*
* @param x0 the <em>x</em>-coordinate of one endpoint
* @param y0 the <em>y</em>-coordinate of one endpoint
* @param x1 the <em>x</em>-coordinate of the other endpoint
* @param y1 the <em>y</em>-coordinate of the other endpoint
*/
public static void line(double x0, double y0, double x1, double y1) {
offscreen.draw(new Line2D.Double(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1)));
draw();
}
/**
* Draws one pixel at (<em>x</em>, <em>y</em>).
* This method is private because pixels depend on the display.
* To achieve the same effect, set the pen radius to 0 and call {@code point()}.
*
* @param x the <em>x</em>-coordinate of the pixel
* @param y the <em>y</em>-coordinate of the pixel
*/
private static void pixel(double x, double y) {
offscreen.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1);
}
/**
* Draws a point centered at (<em>x</em>, <em>y</em>).
* The point is a filled circle whose radius is equal to the pen radius.
* To draw a single-pixel point, first set the pen radius to 0.
*
* @param x the <em>x</em>-coordinate of the point
* @param y the <em>y</em>-coordinate of the point
*/
public static void point(double x, double y) {
double xs = scaleX(x);
double ys = scaleY(y);
double r = penRadius;
float scaledPenRadius = (float) (r * DEFAULT_SIZE);
// double ws = factorX(2*r);
// double hs = factorY(2*r);
// if (ws <= 1 && hs <= 1) pixel(x, y);
if (scaledPenRadius <= 1) pixel(x, y);
else offscreen.fill(new Ellipse2D.Double(xs - scaledPenRadius/2, ys - scaledPenRadius/2,
scaledPenRadius, scaledPenRadius));
draw();
}
/**
* Draws a circle of the specified radius, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the circle
* @param y the <em>y</em>-coordinate of the center of the circle
* @param radius the radius of the circle
* @throws IllegalArgumentException if {@code radius} is negative
*/
public static void circle(double x, double y, double radius) {
if (!(radius >= 0)) throw new IllegalArgumentException("radius must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*radius);
double hs = factorY(2*radius);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a filled circle of the specified radius, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the circle
* @param y the <em>y</em>-coordinate of the center of the circle
* @param radius the radius of the circle
* @throws IllegalArgumentException if {@code radius} is negative
*/
public static void filledCircle(double x, double y, double radius) {
if (!(radius >= 0)) throw new IllegalArgumentException("radius must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*radius);
double hs = factorY(2*radius);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws an ellipse with the specified semimajor and semiminor axes,
* centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the ellipse
* @param y the <em>y</em>-coordinate of the center of the ellipse
* @param semiMajorAxis is the semimajor axis of the ellipse
* @param semiMinorAxis is the semiminor axis of the ellipse
* @throws IllegalArgumentException if either {@code semiMajorAxis}
* or {@code semiMinorAxis} is negative
*/
public static void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) {
if (!(semiMajorAxis >= 0)) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative");
if (!(semiMinorAxis >= 0)) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*semiMajorAxis);
double hs = factorY(2*semiMinorAxis);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws an ellipse with the specified semimajor and semiminor axes,
* centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the ellipse
* @param y the <em>y</em>-coordinate of the center of the ellipse
* @param semiMajorAxis is the semimajor axis of the ellipse
* @param semiMinorAxis is the semiminor axis of the ellipse
* @throws IllegalArgumentException if either {@code semiMajorAxis}
* or {@code semiMinorAxis} is negative
*/
public static void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) {
if (!(semiMajorAxis >= 0)) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative");
if (!(semiMinorAxis >= 0)) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*semiMajorAxis);
double hs = factorY(2*semiMinorAxis);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a circular arc of the specified radius,
* centered at (<em>x</em>, <em>y</em>), from angle1 to angle2 (in degrees).
*
* @param x the <em>x</em>-coordinate of the center of the circle
* @param y the <em>y</em>-coordinate of the center of the circle
* @param radius the radius of the circle
* @param angle1 the starting angle. 0 would mean an arc beginning at 3 o'clock.
* @param angle2 the angle at the end of the arc. For example, if
* you want a 90 degree arc, then angle2 should be angle1 + 90.
* @throws IllegalArgumentException if {@code radius} is negative
*/
public static void arc(double x, double y, double radius, double angle1, double angle2) {
if (radius < 0) throw new IllegalArgumentException("arc radius must be nonnegative");
while (angle2 < angle1) angle2 += 360;
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*radius);
double hs = factorY(2*radius);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Arc2D.Double(xs - ws/2, ys - hs/2, ws, hs, angle1, angle2 - angle1, Arc2D.OPEN));
draw();
}
/**
* Draws a square of side length 2r, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the square
* @param y the <em>y</em>-coordinate of the center of the square
* @param halfLength one half the length of any side of the square
* @throws IllegalArgumentException if {@code halfLength} is negative
*/
public static void square(double x, double y, double halfLength) {
if (!(halfLength >= 0)) throw new IllegalArgumentException("half length must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*halfLength);
double hs = factorY(2*halfLength);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a filled square of the specified size, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the square
* @param y the <em>y</em>-coordinate of the center of the square
* @param halfLength one half the length of any side of the square
* @throws IllegalArgumentException if {@code halfLength} is negative
*/
public static void filledSquare(double x, double y, double halfLength) {
if (!(halfLength >= 0)) throw new IllegalArgumentException("half length must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*halfLength);
double hs = factorY(2*halfLength);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a rectangle of the specified size, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the rectangle
* @param y the <em>y</em>-coordinate of the center of the rectangle
* @param halfWidth one half the width of the rectangle
* @param halfHeight one half the height of the rectangle
* @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative
*/
public static void rectangle(double x, double y, double halfWidth, double halfHeight) {
if (!(halfWidth >= 0)) throw new IllegalArgumentException("half width must be nonnegative");
if (!(halfHeight >= 0)) throw new IllegalArgumentException("half height must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*halfWidth);
double hs = factorY(2*halfHeight);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a filled rectangle of the specified size, centered at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the center of the rectangle
* @param y the <em>y</em>-coordinate of the center of the rectangle
* @param halfWidth one half the width of the rectangle
* @param halfHeight one half the height of the rectangle
* @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative
*/
public static void filledRectangle(double x, double y, double halfWidth, double halfHeight) {
if (!(halfWidth >= 0)) throw new IllegalArgumentException("half width must be nonnegative");
if (!(halfHeight >= 0)) throw new IllegalArgumentException("half height must be nonnegative");
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2*halfWidth);
double hs = factorY(2*halfHeight);
if (ws <= 1 && hs <= 1) pixel(x, y);
else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
draw();
}
/**
* Draws a polygon with the vertices
* (<em>x</em><sub>0</sub>, <em>y</em><sub>0</sub>),
* (<em>x</em><sub>1</sub>, <em>y</em><sub>1</sub>), ...,
* (<em>x</em><sub><em>n</em>–1</sub>, <em>y</em><sub><em>n</em>–1</sub>).
*
* @param x an array of all the <em>x</em>-coordinates of the polygon
* @param y an array of all the <em>y</em>-coordinates of the polygon
* @throws IllegalArgumentException unless {@code x[]} and {@code y[]}
* are of the same length
*/
public static void polygon(double[] x, double[] y) {
if (x == null) throw new IllegalArgumentException("x-coordinate array is null");
if (y == null) throw new IllegalArgumentException("y-coordinate array is null");
int n1 = x.length;
int n2 = y.length;
if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length");
int n = n1;
if (n == 0) return;
GeneralPath path = new GeneralPath();
path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0]));
for (int i = 0; i < n; i++)
path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i]));
path.closePath();
offscreen.draw(path);
draw();
}
/**
* Draws a polygon with the vertices
* (<em>x</em><sub>0</sub>, <em>y</em><sub>0</sub>),
* (<em>x</em><sub>1</sub>, <em>y</em><sub>1</sub>), ...,
* (<em>x</em><sub><em>n</em>–1</sub>, <em>y</em><sub><em>n</em>–1</sub>).
*
* @param x an array of all the <em>x</em>-coordinates of the polygon
* @param y an array of all the <em>y</em>-coordinates of the polygon
* @throws IllegalArgumentException unless {@code x[]} and {@code y[]}
* are of the same length
*/
public static void filledPolygon(double[] x, double[] y) {
if (x == null) throw new IllegalArgumentException("x-coordinate array is null");
if (y == null) throw new IllegalArgumentException("y-coordinate array is null");
int n1 = x.length;
int n2 = y.length;
if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length");
int n = n1;
if (n == 0) return;
GeneralPath path = new GeneralPath();
path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0]));
for (int i = 0; i < n; i++)
path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i]));
path.closePath();
offscreen.fill(path);
draw();
}
/***************************************************************************
* Drawing images.
***************************************************************************/
// get an image from the given filename
private static Image getImage(String filename) {
if (filename == null) throw new IllegalArgumentException();
// to read from file
ImageIcon icon = new ImageIcon(filename);
// try to read from URL
if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
try {
URL url = new URL(filename);
icon = new ImageIcon(url);
}
catch (MalformedURLException e) {
/* not a url */
}
}
// in case file is inside a .jar (classpath relative to StdDraw)
if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
URL url = StdDraw.class.getResource(filename);
if (url != null)
icon = new ImageIcon(url);
}
// in case file is inside a .jar (classpath relative to root of jar)
if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
URL url = StdDraw.class.getResource("/" + filename);
if (url == null) throw new IllegalArgumentException("image " + filename + " not found");
icon = new ImageIcon(url);
}
return icon.getImage();
}
/***************************************************************************
* [Summer 2016] Should we update to use ImageIO instead of ImageIcon()?
* Seems to have some issues loading images on some systems
* and slows things down on other systems.
* especially if you don't call ImageIO.setUseCache(false)
* One advantage is that it returns a BufferedImage.
***************************************************************************/
/*
private static BufferedImage getImage(String filename) {
if (filename == null) throw new IllegalArgumentException();
// from a file or URL
try {
URL url = new URL(filename);
BufferedImage image = ImageIO.read(url);
return image;
}
catch (IOException e) {
// ignore
}
// in case file is inside a .jar (classpath relative to StdDraw)
try {
URL url = StdDraw.class.getResource(filename);
BufferedImage image = ImageIO.read(url);
return image;
}
catch (IOException e) {
// ignore
}
// in case file is inside a .jar (classpath relative to root of jar)
try {
URL url = StdDraw.class.getResource("/" + filename);
BufferedImage image = ImageIO.read(url);
return image;
}
catch (IOException e) {
// ignore
}
throw new IllegalArgumentException("image " + filename + " not found");
}
*/
/**
* Draws the specified image centered at (<em>x</em>, <em>y</em>).
* The supported image formats are JPEG, PNG, and GIF.
* As an optimization, the picture is cached, so there is no performance
* penalty for redrawing the same image multiple times (e.g., in an animation).
* However, if you change the picture file after drawing it, subsequent
* calls will draw the original picture.
*
* @param x the center <em>x</em>-coordinate of the image
* @param y the center <em>y</em>-coordinate of the image
* @param filename the name of the image/picture, e.g., "ball.gif"
* @throws IllegalArgumentException if the image filename is invalid
*/
public static void picture(double x, double y, String filename) {
if (DO_NOTHING) {
return;
}
// BufferedImage image = getImage(filename);
Image image = getImage(filename);
double xs = scaleX(x);
double ys = scaleY(y);
// int ws = image.getWidth(); // can call only if image is a BufferedImage
// int hs = image.getHeight();
int ws = image.getWidth(null);
int hs = image.getHeight(null);
if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null);
draw();
}
/**
* Draws the specified image centered at (<em>x</em>, <em>y</em>),
* rotated given number of degrees.
* The supported image formats are JPEG, PNG, and GIF.
*
* @param x the center <em>x</em>-coordinate of the image
* @param y the center <em>y</em>-coordinate of the image
* @param filename the name of the image/picture, e.g., "ball.gif"
* @param degrees is the number of degrees to rotate counterclockwise
* @throws IllegalArgumentException if the image filename is invalid
*/
public static void picture(double x, double y, String filename, double degrees) {
if (DO_NOTHING) {
return;
}
// BufferedImage image = getImage(filename);
Image image = getImage(filename);
double xs = scaleX(x);
double ys = scaleY(y);
// int ws = image.getWidth(); // can call only if image is a BufferedImage
// int hs = image.getHeight();
int ws = image.getWidth(null);
int hs = image.getHeight(null);
if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
offscreen.rotate(Math.toRadians(-degrees), xs, ys);
offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null);
offscreen.rotate(Math.toRadians(+degrees), xs, ys);
draw();
}
/**
* Draws the specified image centered at (<em>x</em>, <em>y</em>),
* rescaled to the specified bounding box.
* The supported image formats are JPEG, PNG, and GIF.
*
* @param x the center <em>x</em>-coordinate of the image
* @param y the center <em>y</em>-coordinate of the image
* @param filename the name of the image/picture, e.g., "ball.gif"
* @param scaledWidth the width of the scaled image (in screen coordinates)
* @param scaledHeight the height of the scaled image (in screen coordinates)
* @throws IllegalArgumentException if either {@code scaledWidth}
* or {@code scaledHeight} is negative
* @throws IllegalArgumentException if the image filename is invalid
*/
public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight) {
Image image = getImage(filename);
if (scaledWidth < 0) throw new IllegalArgumentException("width is negative: " + scaledWidth);
if (scaledHeight < 0) throw new IllegalArgumentException("height is negative: " + scaledHeight);
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(scaledWidth);
double hs = factorY(scaledHeight);
if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
if (ws <= 1 && hs <= 1) pixel(x, y);
else {
offscreen.drawImage(image, (int) Math.round(xs - ws/2.0),
(int) Math.round(ys - hs/2.0),
(int) Math.round(ws),
(int) Math.round(hs), null);
}
draw();
}
/**
* Draws the specified image centered at (<em>x</em>, <em>y</em>), rotated
* given number of degrees, and rescaled to the specified bounding box.
* The supported image formats are JPEG, PNG, and GIF.
*
* @param x the center <em>x</em>-coordinate of the image
* @param y the center <em>y</em>-coordinate of the image
* @param filename the name of the image/picture, e.g., "ball.gif"
* @param scaledWidth the width of the scaled image (in screen coordinates)
* @param scaledHeight the height of the scaled image (in screen coordinates)
* @param degrees is the number of degrees to rotate counterclockwise
* @throws IllegalArgumentException if either {@code scaledWidth}
* or {@code scaledHeight} is negative
* @throws IllegalArgumentException if the image filename is invalid
*/
public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight, double degrees) {
if (DO_NOTHING) {
return;
}
if (scaledWidth < 0) throw new IllegalArgumentException("width is negative: " + scaledWidth);
if (scaledHeight < 0) throw new IllegalArgumentException("height is negative: " + scaledHeight);
Image image = getImage(filename);
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(scaledWidth);
double hs = factorY(scaledHeight);
if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
if (ws <= 1 && hs <= 1) pixel(x, y);
offscreen.rotate(Math.toRadians(-degrees), xs, ys);
offscreen.drawImage(image, (int) Math.round(xs - ws/2.0),
(int) Math.round(ys - hs/2.0),
(int) Math.round(ws),
(int) Math.round(hs), null);
offscreen.rotate(Math.toRadians(+degrees), xs, ys);
draw();
}
/***************************************************************************
* Drawing text.
***************************************************************************/
/**
* Write the given text string in the current font, centered at (<em>x</em>, <em>y</em>).
*
* @param x the center <em>x</em>-coordinate of the text
* @param y the center <em>y</em>-coordinate of the text
* @param text the text to write
*/
public static void text(double x, double y, String text) {
if (text == null) throw new IllegalArgumentException();
offscreen.setFont(font);
FontMetrics metrics = offscreen.getFontMetrics();
double xs = scaleX(x);
double ys = scaleY(y);
int ws = metrics.stringWidth(text);
int hs = metrics.getDescent();
offscreen.drawString(text, (float) (xs - ws/2.0), (float) (ys + hs));
draw();
}
/**
* Write the given text string in the current font, centered at (<em>x</em>, <em>y</em>) and
* rotated by the specified number of degrees.
* @param x the center <em>x</em>-coordinate of the text
* @param y the center <em>y</em>-coordinate of the text
* @param text the text to write
* @param degrees is the number of degrees to rotate counterclockwise
*/
public static void text(double x, double y, String text, double degrees) {
if (text == null) throw new IllegalArgumentException();
double xs = scaleX(x);
double ys = scaleY(y);
offscreen.rotate(Math.toRadians(-degrees), xs, ys);
text(x, y, text);
offscreen.rotate(Math.toRadians(+degrees), xs, ys);
}
/**
* Write the given text string in the current font, left-aligned at (<em>x</em>, <em>y</em>).
* @param x the <em>x</em>-coordinate of the text
* @param y the <em>y</em>-coordinate of the text
* @param text the text
*/
public static void textLeft(double x, double y, String text) {
if (text == null) throw new IllegalArgumentException();
offscreen.setFont(font);
FontMetrics metrics = offscreen.getFontMetrics();
double xs = scaleX(x);
double ys = scaleY(y);
int hs = metrics.getDescent();
offscreen.drawString(text, (float) xs, (float) (ys + hs));
draw();
}
/**
* Write the given text string in the current font, right-aligned at (<em>x</em>, <em>y</em>).
*
* @param x the <em>x</em>-coordinate of the text
* @param y the <em>y</em>-coordinate of the text
* @param text the text to write
*/
public static void textRight(double x, double y, String text) {
if (text == null) throw new IllegalArgumentException();
offscreen.setFont(font);
FontMetrics metrics = offscreen.getFontMetrics();
double xs = scaleX(x);
double ys = scaleY(y);
int ws = metrics.stringWidth(text);
int hs = metrics.getDescent();
offscreen.drawString(text, (float) (xs - ws), (float) (ys + hs));
draw();
}
/**
* Copies the offscreen buffer to the onscreen buffer, pauses for t milliseconds
* and enables double buffering.
* @param t number of milliseconds
* @deprecated replaced by {@link #enableDoubleBuffering()}, {@link #show()}, and {@link #pause(int t)}
*/
@Deprecated
public static void show(int t) {
show();
pause(t);
enableDoubleBuffering();
}
/**
* Pause for t milliseconds. This method is intended to support computer animations.
* @param t number of milliseconds
*/
public static void pause(int t) {
if (NO_PAUSE) {
return;
}
if (DO_NOTHING) {
return;
}
try {
Thread.sleep(t);
}
catch (InterruptedException e) {
System.out.println("Error sleeping");
}
}
/**
* Copies offscreen buffer to onscreen buffer. There is no reason to call
* this method unless double buffering is enabled.
*/
public static void show() {
if (DO_NOTHING) {
return;
}
onscreen.drawImage(offscreenImage, 0, 0, null);
frame.repaint();
}
// draw onscreen if defer is false
private static void draw() {
if (DO_NOTHING) {
return;
}
if (!defer) show();
}
/**
* Enable double buffering. All subsequent calls to
* drawing methods such as {@code line()}, {@code circle()},
* and {@code square()} will be deffered until the next call
* to show(). Useful for animations.
*/
public static void enableDoubleBuffering() {
defer = true;
}
/**
* Disable double buffering. All subsequent calls to
* drawing methods such as {@code line()}, {@code circle()},
* and {@code square()} will be displayed on screen when called.
* This is the default.
*/
public static void disableDoubleBuffering() {
defer = false;
}
/***************************************************************************
* Save drawing to a file.
***************************************************************************/
/***
* Returns the image displayed by {@code StdDraw}.
* @return the image currently displayed
*/
public static BufferedImage getImage() {
return onscreenImage;
}
/**
* Saves the drawing to using the specified filename.
* The supported image formats are JPEG and PNG;
* the filename suffix must be {@code .jpg} or {@code .png}.
*
* @param filename the name of the file with one of the required suffixes
*/
public static void save(String filename) {
if (filename == null) throw new IllegalArgumentException();
File file = new File(filename);
String suffix = filename.substring(filename.lastIndexOf('.') + 1);
// png files
if ("png".equalsIgnoreCase(suffix)) {
try {
ImageIO.write(onscreenImage, suffix, file);
}
catch (IOException e) {
e.printStackTrace();
}
}
// need to change from ARGB to RGB for JPEG
// reference: http://archives.java.sun.com/cgi-bin/wa?A2=ind0404&L=java2d-interest&D=0&P=2727
else if ("jpg".equalsIgnoreCase(suffix)) {
WritableRaster raster = onscreenImage.getRaster();
WritableRaster newRaster;
newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2});
DirectColorModel cm = (DirectColorModel) onscreenImage.getColorModel();
DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(),
cm.getRedMask(),
cm.getGreenMask(),
cm.getBlueMask());
BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null);
try {
ImageIO.write(rgbBuffer, suffix, file);
}
catch (IOException e) {
e.printStackTrace();
}
}
else {
System.out.println("Invalid image file type: " + suffix);
}
}
/**
* This method cannot be called directly.
*/
@Override
public void actionPerformed(ActionEvent e) {
FileDialog chooser = new FileDialog(StdDraw.frame, "Use a .png or .jpg extension", FileDialog.SAVE);
chooser.setVisible(true);
String filename = chooser.getFile();
if (filename != null) {
StdDraw.save(chooser.getDirectory() + File.separator + chooser.getFile());
}
}
/***************************************************************************
* Mouse interactions.
***************************************************************************/
/**
* Returns true if the mouse is being pressed.
*
* @return {@code true} if the mouse is being pressed; {@code false} otherwise
*/
public static boolean isMousePressed() {
synchronized (mouseLock) {
return isMousePressed;
}
}
/**
* Returns true if the mouse is being pressed.
*
* @return {@code true} if the mouse is being pressed; {@code false} otherwise
* @deprecated replaced by {@link #isMousePressed()}
*/
@Deprecated
public static boolean mousePressed() {
synchronized (mouseLock) {
return isMousePressed;
}
}
/**
* Returns the <em>x</em>-coordinate of the mouse.
*
* @return the <em>x</em>-coordinate of the mouse
*/
public static double mouseX() {
synchronized (mouseLock) {
return mouseX;
}
}
/**
* Returns the <em>y</em>-coordinate of the mouse.
*
* @return <em>y</em>-coordinate of the mouse
*/
public static double mouseY() {
synchronized (mouseLock) {
return mouseY;
}
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseClicked(MouseEvent e) {
// this body is intentionally left empty
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseEntered(MouseEvent e) {
// this body is intentionally left empty
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseExited(MouseEvent e) {
// this body is intentionally left empty
}
/**
* This method cannot be called directly.
*/
@Override
public void mousePressed(MouseEvent e) {
synchronized (mouseLock) {
mouseX = StdDraw.userX(e.getX());
mouseY = StdDraw.userY(e.getY());
isMousePressed = true;
}
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseReleased(MouseEvent e) {
synchronized (mouseLock) {
isMousePressed = false;
}
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseDragged(MouseEvent e) {
synchronized (mouseLock) {
mouseX = StdDraw.userX(e.getX());
mouseY = StdDraw.userY(e.getY());
}
}
/**
* This method cannot be called directly.
*/
@Override
public void mouseMoved(MouseEvent e) {
synchronized (mouseLock) {
mouseX = StdDraw.userX(e.getX());
mouseY = StdDraw.userY(e.getY());
}
}
/***************************************************************************
* Keyboard interactions.
***************************************************************************/
/**
* Returns true if the user has typed a key (that has not yet been processed).
*
* @return {@code true} if the user has typed a key (that has not yet been processed
* by {@link #nextKeyTyped()}; {@code false} otherwise
*/
public static boolean hasNextKeyTyped() {
synchronized (keyLock) {
return !keysTyped.isEmpty();
}
}
/**
* Returns the next key that was typed by the user (that your program has not already processed).
* This method should be preceded by a call to {@link #hasNextKeyTyped()} to ensure
* that there is a next key to process.
* This method returns a Unicode character corresponding to the key
* typed (such as {@code 'a'} or {@code 'A'}).
* It cannot identify action keys (such as F1 and arrow keys)
* or modifier keys (such as control).
*
* @return the next key typed by the user (that your program has not already processed).
* @throws NoSuchElementException if there is no remaining key
*/
public static char nextKeyTyped() {
synchronized (keyLock) {
if (keysTyped.isEmpty()) {
throw new NoSuchElementException("your program has already processed all keystrokes");
}
return keysTyped.remove(keysTyped.size() - 1);
// return keysTyped.removeLast();
}
}
/**
* Returns true if the given key is being pressed.
* <p>
* This method takes the keycode (corresponding to a physical key)
* as an argument. It can handle action keys
* (such as F1 and arrow keys) and modifier keys (such as shift and control).
* See {@link KeyEvent} for a description of key codes.
*
* @param keycode the key to check if it is being pressed
* @return {@code true} if {@code keycode} is currently being pressed;
* {@code false} otherwise
*/
public static boolean isKeyPressed(int keycode) {
synchronized (keyLock) {
return keysDown.contains(keycode);
}
}
/**
* This method cannot be called directly.
*/
@Override
public void keyTyped(KeyEvent e) {
synchronized (keyLock) {
keysTyped.addFirst(e.getKeyChar());
}
}
/**
* This method cannot be called directly.
*/
@Override
public void keyPressed(KeyEvent e) {
synchronized (keyLock) {
keysDown.add(e.getKeyCode());
}
}
/**
* This method cannot be called directly.
*/
@Override
public void keyReleased(KeyEvent e) {
synchronized (keyLock) {
keysDown.remove(e.getKeyCode());
}
}
/**
* Test client.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
StdDraw.square(0.2, 0.8, 0.1);
StdDraw.filledSquare(0.8, 0.8, 0.2);
StdDraw.circle(0.8, 0.2, 0.2);
StdDraw.setPenColor(StdDraw.BOOK_RED);
StdDraw.setPenRadius(0.02);
StdDraw.arc(0.8, 0.2, 0.1, 200, 45);
// draw a blue diamond
StdDraw.setPenRadius();
StdDraw.setPenColor(StdDraw.BOOK_BLUE);
double[] x = { 0.1, 0.2, 0.3, 0.2 };
double[] y = { 0.2, 0.3, 0.2, 0.1 };
StdDraw.filledPolygon(x, y);
// text
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.text(0.2, 0.5, "black text");
StdDraw.setPenColor(StdDraw.WHITE);
StdDraw.text(0.8, 0.8, "white text");
}
}
\ No newline at end of file
0.5
0.5
0.5
0.5
0.5
0.498
0.498
0.498
0.497004
0.496008
0.496008
0.496008
0.495511992
0.494519976
0.494023968
0.494023968
0.49377695601599997
0.49303592006400004
0.492294884112
0.492047872128
0.49192486015996795
0.49143281228784
0.490694740479648
0.49020269260752
0.490018420679408
0.48971212087900834
0.489099521278209
0.4884869216774097
0.4881501144168901
0.4879058096960913
0.4874481977942942
0.4868380485918981
0.4863652439749613
0.4860758502082647
0.485726295730212
0.4851945507003238
0.484655239698296
0.4842756649032465
0.4839574686773614
0.48351858152240684
0.48298519561851266
0.4825275904915682
0.48218010052314275
0.4818030729994846
0.4813188810161779
0.48082536748282023
0.4804244301253261
0.4800636204142685
0.4796347330998
0.4791478357525011
0.4787023992088569
0.4783230491687181
0.4779297800500061
0.47747371928844595
0.4770094170107563
0.4765986732920323
0.4762139089509247
0.4757909426705491
0.4753326018770027
0.4748968289707887
0.4745006659569926
0.474098416107494
0.47365952518468085
0.4732142565622001
0.47279995247403506
0.4724023428681143
0.47198345476350306
0.47154314330994673
0.4711150761000451
0.47071074308039035
0.47030412722054543
0.46987624584057797
0.4694437932661759
0.46902925795185685
0.468625405409866
0.4682098257844394
0.4677813794751634
0.46735957950658036
0.46695202235413796
0.4665439451347641
0.4661236202192822
0.46570019757290837
0.4652871777266377
0.46488099180947323
0.4644684475463151
0.4640482612605109
0.463631712899174
0.46322374842898323
0.4628160207991826
0.46240132098579934
0.46198462713152305
0.4615740197414223
0.4611678050756266
0.460758236208921
0.46034420216242655
0.4599322061427268
0.45952542875889035
0.4591191685597047
0.4587090143089311
0.45829765133596634
0.45788990218100534
\ No newline at end of file
0.3
0.2
0.1
0
-0.1
-0.2
0.14940000000000003
0.049800000000000004
-0.049800000000000004
-0.14940000000000003
0.024401999999999993
0.1984032
0.09920160000000001
0.0
-0.09920160000000001
-0.06224900400000002
0.11095698959999999
0.1482071904
0.049402396800000005
-0.049402396800000005
-0.080402400792
0.024256576828799985
0.12906376164
0.0984095744256
0.0
-0.064642789200816
-0.027960620333673608
0.07635352855746239
0.1132817213606688
0.0490079680639488
-0.032192109022006365
-0.04611649794817582
0.02409966829544681
0.09443835445922934
0.08082026533345957
0.00837429780288733
-0.03899768627115073
-0.010964381167059048
0.05903193533182872
0.08727879265675909
0.044418892441900754
-0.015250447457195174
-0.02488110958422847
0.023937641974055294
0.07286274253831672
0.0655854471791326
0.014525885602383378
-0.019985515406628972
-4.6984686986624154E-4
0.04820659148716126
0.06894719847928976
0.03989544372519496
-0.002718895642514306
-0.010186770413694616
0.02377289881941292
0.05834258740329261
0.05420363581783339
0.018513920945174966
-0.006427021695992043
0.006765891946047715
0.04089351213890736
0.05604801916412075
0.03621334326797816
0.006019275826093095
1.6875738452772472E-4
0.023734383234307627
0.04827688258890799
0.04594615849118526
0.021031844308847487
0.0030816405388891686
0.011903764028180004
0.035861610379961384
0.04692307445788644
0.033355045394416305
0.012008515454172855
0.007462731474400449
0.02378715645525441
0.04122677304924821
0.039978503686446766
0.022591053302597402
0.009696680970429505
0.01556244418896812
0.0323769368932423
0.0404402278143761
0.031159639380543995
0.0160792916679674
0.012579044329380019
0.02387381177894079
0.03626294802439396
0.03565673386307021
0.023524987662158676
0.014271851326679016
0.018153522341943763
0.029948106382060705
0.03581600157995716
0.029472497319563983
0.01882282581644117
\ No newline at end of file
0.7825462456
-0.4561245625
0.3245324556
-0.9712345674
-0.5027592834
-0.1673752768
0.2364378668
0.8743574274
-0.7452743574
-0.0655328692362
-0.3220576516764
-0.7340489376984
-0.33372701097959995
0.03439316982000001
0.5531760565116
0.06428336885999998
-0.2901927468896676
0.048318514215904794
-0.1930200794144748
-0.5259410815086503
-0.5317524224416439
-0.14906825289748077
0.29260947471313675
0.30749479383505673
-0.11250287025877449
-0.12045336787153388
-0.07206137946888785
-0.35804265813971625
-0.5267313649672465
-0.33904869631888407
0.07148352846419669
0.2988519257370003
0.09710597794098856
-0.11601220658889357
-0.09587234417553002
-0.21419181072908483
-0.44061746350726744
-0.43115847052049305
-0.1332474535916343
0.1844270561921961
0.19718703603163845
-0.009415301866656692
-0.10551850628068295
-0.1544119491424982
-0.3260950185697034
-0.4341444151458247
-0.2810741502078394
0.02548744209507977
0.19004381792746963
0.09351032361416092
-0.05723703645737514
-0.12944536680074423
-0.23929246992067638
-0.37859923799033296
-0.35617884554612467
-0.12728218064015429
0.1073345674912296
0.14120996248773202
0.018064097004079318
-0.09296783682254343
-0.18363144268726744
-0.30771007053968263
-0.36591948560115595
-0.24076359104076692
-0.00993391134816449
0.12377517592952289
0.07931848162692205
-0.03730206242959513
-0.13774644119588583
-0.24468807358702113
-0.3354675189581376
-0.3021281721676776
-0.12484735618968784
0.05669294976151648
0.10114064146310957
0.02092417676026881
-0.08717415480548953
-0.19045238836188766
-0.28891748508748905
-0.31752265418065595
-0.212633813121968
-0.03394089440122934
0.07860112842986376
0.06078827947524243
-0.032992489066519914
-0.13825801849735384
-0.2387261969777896
-0.3020071893555362
-0.26401792071670677
-0.12279420434655226
0.02224079654625994
0.06941592513674287
0.013842303623543812
-0.08528275276680913
-0.18773813930662142
-0.26928522639399627
-0.281880504815977
-0.19263243828150298
-0.05007559708454557
0.0456450473981354
\ No newline at end of file
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.helpers.Inspection;
import edu.caltech.cs2.helpers.Reflection;
import edu.caltech.cs2.helpers.RuntimeInstrumentation;
import edu.caltech.cs2.interfaces.ICollection;
import edu.caltech.cs2.interfaces.IQueue;
import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.IStack;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import static edu.caltech.cs2.project03.Project03TestOrdering.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@Tag("C")
public class ArrayDequeTests implements DequeTests, StackTests, QueueTests {
private static String ARRAY_DEQUE_SOURCE ="src/edu/caltech/cs2/datastructures/ArrayDeque.java";
private Constructor arrayDequeConstructor = Reflection.getConstructor(ArrayDeque.class);
public ICollection<Object> newCollection() {
return Reflection.newInstance(arrayDequeConstructor);
}
public IDeque<Object> newDeque() {
return Reflection.newInstance(arrayDequeConstructor);
}
public IStack<Object> newStack() {
return Reflection.newInstance(arrayDequeConstructor);
}
public IQueue<Object> newQueue() {
return Reflection.newInstance(arrayDequeConstructor);
}
public IQueue<Object> newQueue(int size) {
return newQueue();
}
// ARRAYDEQUE-SPECIFIC TESTS ----------------------------------------
@Order(classSpecificTestLevel)
@DisplayName("Does not use or import disallowed classes")
@Test
public void testForInvalidClasses() {
List<String> regexps = List.of("java\\.util\\.(?!Iterator)", "java\\.lang\\.reflect", "java\\.io");
Inspection.assertNoImportsOf(ARRAY_DEQUE_SOURCE, regexps);
Inspection.assertNoUsageOf(ARRAY_DEQUE_SOURCE, regexps);
}
@Order(classSpecificTestLevel)
@DisplayName("There is an integer default capacity static field and an integer default grow factor static field")
@Test
public void testConstantFields() {
Reflection.assertFieldsEqualTo(ArrayDeque.class, "static", 2);
Stream<Field> fields = Reflection.getFields(ArrayDeque.class);
fields.filter(Reflection.hasModifier("static")).forEach((field) -> {
Reflection.checkFieldModifiers(field, List.of("private", "static", "final"));
assertEquals(field.getType(), int.class, "static fields must be of type int");
});
}
@Order(classSpecificTestLevel)
@DisplayName("The overall number of fields is small")
@Test
public void testSmallNumberOfFields() {
Reflection.assertFieldsLessThan(ArrayDeque.class, "private", 5);
}
@Order(classSpecificTestLevel)
@DisplayName("There are no public fields")
@Test
public void testNoPublicFields() {
Reflection.assertNoPublicFields(ArrayDeque.class);
}
@Order(classSpecificTestLevel)
@DisplayName("The public interface is correct")
@Test
public void testPublicInterface() {
Reflection.assertPublicInterface(ArrayDeque.class, List.of(
"addFront",
"addBack",
"removeFront",
"removeBack",
"enqueue",
"dequeue",
"push",
"pop",
"peek",
"peekFront",
"peekBack",
"iterator",
"size",
"toString"
));
}
@Order(classSpecificTestLevel)
@DisplayName("Uses this(...) notation in all but one constructor")
@Test
public void testForThisConstructors() {
Inspection.assertConstructorHygiene(ARRAY_DEQUE_SOURCE);
}
// TOSTRING TESTS ---------------------------------------------------
@Order(toStringTestLevel)
@DisplayName("toString is correctly overridden")
@Test
public void testToStringOverride() {
Reflection.assertMethodCorrectlyOverridden(ArrayDeque.class, "toString");
}
@Order(toStringTestLevel)
@DisplayName("toString() matches java.util.ArrayDeque")
@ParameterizedTest(name = "Test toString() on [{arguments}]")
@ValueSource(strings = {
"0, 1, 2, 3", "5, 4, 3, 2, 1", "8, 3, 5, 7, 4, 3, 12, 12, 1"
})
public void testToString(String inputs) {
java.util.ArrayDeque<String> reference = new java.util.ArrayDeque<String>();
edu.caltech.cs2.datastructures.ArrayDeque<String> me = new edu.caltech.cs2.datastructures.ArrayDeque<>();
for (String value : inputs.trim().split(", ")) {
assertEquals(reference.toString(), me.toString(), "toString outputs should be the same");
reference.addLast(value);
me.addBack(value);
}
}
// TIME COMPLEXITY TESTS ------------------------------------------------
@Order(complexityTestLevel)
@DisplayName("addFront() and removeFront() take linear time")
@Test()
public void testFrontDequeOperationComplexity() {
Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
IDeque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numElements; i++) {
q.addFront(i);
}
return q;
};
Consumer<IDeque<Integer>> addFront = (IDeque<Integer> q) -> q.addFront(0);
Consumer<IDeque<Integer>> removeFront = (IDeque<Integer> q) -> q.removeFront();
RuntimeInstrumentation.assertAtMost("addFront", RuntimeInstrumentation.ComplexityType.LINEAR, provide, addFront, 8);
RuntimeInstrumentation.assertAtMost("removeFront", RuntimeInstrumentation.ComplexityType.LINEAR, provide, removeFront, 8);
}
@Order(complexityTestLevel)
@DisplayName("addBack() and removeBack() take linear time")
@Test
public void testBackDequeOperationComplexity() {
Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
IDeque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numElements; i++) {
q.addBack(i);
}
return q;
};
Consumer<IDeque<Integer>> addBack = (IDeque<Integer> q) -> q.addBack(0);
Consumer<IDeque<Integer>> removeBack = (IDeque<Integer> q) -> q.removeBack();
RuntimeInstrumentation.assertAtMost("addBack", RuntimeInstrumentation.ComplexityType.LINEAR, provide, addBack, 8);
RuntimeInstrumentation.assertAtMost("removeBack", RuntimeInstrumentation.ComplexityType.LINEAR, provide, removeBack, 8);
}
@Order(complexityTestLevel)
@DisplayName("enqueue() and dequeue() take linear time")
@Test
public void testQueueOperationComplexity() {
Function<Integer, IQueue<Integer>> provide = (Integer numElements) -> {
IQueue<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numElements; i++) {
q.enqueue(i);
}
return q;
};
Consumer<IQueue<Integer>> enqueue = (IQueue<Integer> q) -> q.enqueue(0);
Consumer<IQueue<Integer>> dequeue = (IQueue<Integer> q) -> q.dequeue();
RuntimeInstrumentation.assertAtMost("enqueue", RuntimeInstrumentation.ComplexityType.LINEAR, provide, enqueue, 8);
RuntimeInstrumentation.assertAtMost("dequeue", RuntimeInstrumentation.ComplexityType.LINEAR, provide, dequeue, 8);
}
@Order(complexityTestLevel)
@DisplayName("push() and pop() take constant time")
@Test
public void testStackOperationComplexity() {
Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
IStack<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numElements; i++) {
q.push(i);
}
return q;
};
Consumer<IStack<Integer>> push = (IStack<Integer> q) -> q.push(0);
Consumer<IStack<Integer>> pop = (IStack<Integer> q) -> q.pop();
RuntimeInstrumentation.assertAtMost("push", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, push, 8);
RuntimeInstrumentation.assertAtMost("pop", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, pop, 8);
}
@Order(complexityTestLevel)
@DisplayName("peek() takes constant time")
@Test
public void testPeekComplexity() {
Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
IStack<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numElements; i++) {
q.push(i);
}
return q;
};
Consumer<IStack<Integer>> peek = (IStack<Integer> q) -> q.peek();
RuntimeInstrumentation.assertAtMost("peek", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peek, 8);
}
@Order(complexityTestLevel)
@DisplayName("peekFront() takes constant time")
@Test()
public void testPeekFrontComplexity() {
Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
IDeque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numElements; i++) {
q.addFront(i);
}
return q;
};
Consumer<IDeque<Integer>> peekFront = (IDeque<Integer> q) -> q.peekFront();
RuntimeInstrumentation.assertAtMost("peekFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekFront, 8);
}
@Order(complexityTestLevel)
@DisplayName("peekBack() takes constant time")
@Test
public void testPeekBackComplexity() {
Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
IDeque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numElements; i++) {
q.addBack(i);
}
return q;
};
Consumer<IDeque<Integer>> peekBack = (IDeque<Integer> q) -> q.peekBack();
RuntimeInstrumentation.assertAtMost("peekBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekBack, 8);
}
}
\ No newline at end of file
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.helpers.Inspection;
import edu.caltech.cs2.helpers.Reflection;
import edu.caltech.cs2.helpers.RuntimeInstrumentation;
import edu.caltech.cs2.interfaces.IFixedSizeQueue;
import edu.caltech.cs2.interfaces.IQueue;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import static edu.caltech.cs2.project03.Project03TestOrdering.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Tag("B")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CircularArrayFixedSizeQueueTests implements FixedSizeQueueTests {
private static String FIXED_QUEUE_SOURCE ="src/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueue.java";
private Constructor circFixedSizeQueueConstructor = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
private int DEFAULT_CAPACITY = 10;
public IQueue<Object> newQueue() {
return Reflection.newInstance(circFixedSizeQueueConstructor, DEFAULT_CAPACITY);
}
public IQueue<Object> newQueue(int capacity) {
return Reflection.newInstance(circFixedSizeQueueConstructor, capacity);
}
public IFixedSizeQueue<Object> newFixedSizeQueue(int capacity) {
return Reflection.newInstance(circFixedSizeQueueConstructor, capacity);
}
// FIXED QUEUE-SPECIFIC TESTS ----------------------------------------
@Order(classSpecificTestLevel)
@DisplayName("Does not use or import disallowed classes")
@Test
public void testForInvalidClasses() {
List<String> regexps = List.of("java\\.util\\.(?!Iterator)", "java\\.lang\\.reflect", "java\\.io");
Inspection.assertNoImportsOf(FIXED_QUEUE_SOURCE, regexps);
Inspection.assertNoUsageOf(FIXED_QUEUE_SOURCE, regexps);
}
@Order(classSpecificTestLevel)
@DisplayName("There are no static fields")
@Test
public void testConstantFields() {
Reflection.assertFieldsEqualTo(CircularArrayFixedSizeQueue.class, "static", 0);
}
@Order(classSpecificTestLevel)
@DisplayName("The overall number of fields is small")
@Test
public void testSmallNumberOfFields() {
Reflection.assertFieldsLessThan(CircularArrayFixedSizeQueue.class, "private", 4);
}
@Order(classSpecificTestLevel)
@DisplayName("There are no public fields")
@Test
public void testNoPublicFields() {
Reflection.assertNoPublicFields(CircularArrayFixedSizeQueue.class);
}
@Order(classSpecificTestLevel)
@DisplayName("The public interface is correct")
@Test
public void testPublicInterface() {
Reflection.assertPublicInterface(CircularArrayFixedSizeQueue.class, List.of(
"enqueue",
"dequeue",
"peek",
"iterator",
"size",
"isFull",
"capacity",
"toString"
));
}
@Order(classSpecificTestLevel)
@DisplayName("Uses this(...) notation in all but one constructor")
@Test
public void testForThisConstructors() {
Inspection.assertConstructorHygiene(FIXED_QUEUE_SOURCE);
}
// TOSTRING TESTS ---------------------------------------------------
@Order(toStringTestLevel)
@DisplayName("toString is correctly overridden")
@Test
public void testToStringOverride() {
Reflection.assertMethodCorrectlyOverridden(ArrayDeque.class, "toString");
}
@Order(toStringTestLevel)
@DisplayName("toString() matches java.util.ArrayDeque")
@ParameterizedTest(name = "Test toString() on [{arguments}]")
@ValueSource(strings = {
"0, 1, 2, 3", "5, 4, 3, 2, 1", "8, 3, 5, 7, 4, 3, 12, 12, 1"
})
public void testToString(String inputs) {
java.util.ArrayDeque<String> reference = new java.util.ArrayDeque<String>();
Constructor c = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
IFixedSizeQueue<String> me = Reflection.newInstance(c, inputs.length());
for (String value : inputs.trim().split(", ")) {
assertEquals(reference.toString(), me.toString(), "toString outputs should be the same");
reference.addLast(value);
me.enqueue(value);
}
}
// TIME COMPLEXITY TESTS ------------------------------------------------
@Order(complexityTestLevel)
@DisplayName("enqueue() and dequeue() take constant time")
@Test()
public void testQueueOperationComplexity() {
Function<Integer, IFixedSizeQueue<Integer>> provide = (Integer numElements) -> {
Constructor c = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
IFixedSizeQueue<Integer> q = Reflection.newInstance(c, numElements*2);
for (int i = 0; i < numElements; i++) {
q.enqueue(i);
}
return q;
};
Consumer<IFixedSizeQueue<Integer>> enqueue = (IFixedSizeQueue<Integer> q) -> q.enqueue(0);
Consumer<IFixedSizeQueue<Integer>> dequeue = (IFixedSizeQueue<Integer> q) -> q.dequeue();
RuntimeInstrumentation.assertAtMost("enqueue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, enqueue, 8);
RuntimeInstrumentation.assertAtMost("dequeue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, dequeue, 8);
}
@Order(complexityTestLevel)
@DisplayName("peek() takes constant time")
@Test()
public void testPeekComplexity() {
Function<Integer, IFixedSizeQueue<Integer>> provide = (Integer numElements) -> {
Constructor c = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
IFixedSizeQueue<Integer> q = Reflection.newInstance(c, numElements*2);
for (int i = 0; i < numElements; i++) {
q.enqueue(i);
}
return q;
};
Consumer<IFixedSizeQueue<Integer>> peek = (IFixedSizeQueue<Integer> q) -> q.peek();
RuntimeInstrumentation.assertAtMost("peek", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peek, 8);
}
}
\ No newline at end of file
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.interfaces.ICollection;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import static edu.caltech.cs2.project03.Project03TestOrdering.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public interface CollectionTests {
ICollection<Object> newCollection();
@Order(collectionTestLevel)
@DisplayName("Simple tests of various ICollection functions")
@ParameterizedTest(name = "Test add(), size(), isEmpty(), contains(), and clear() on [{arguments}]")
@ValueSource(strings = {
"",
"1",
"0, 1, 2, 3",
"5, 4, 3, 2, 1",
"8, 3, 5, 7, 4, 3, 12, 12, 1"
})
default void testCollectionFunctions(String inputs) {
ICollection<Object> impl = newCollection();
List<Object> reference = new java.util.ArrayList<>();
// Check that collection is empty
assertTrue(impl.isEmpty(), "collection should be empty");
// Check that values are not in collection
for (Object value : inputs.trim().split(", ")) {
assertFalse(impl.contains(value), "value should not be contained");
}
// Add all values to collection
for (Object value : inputs.trim().split(", ")) {
impl.add(value);
reference.add(value);
}
// Check that size() and isEmpty() is correct
assertEquals(reference.size(), impl.size(), "sizes should be equal");
assertFalse(impl.isEmpty(), "collection should not be empty");
// Check that values are in collection
for (Object value : inputs.trim().split(", ")) {
assertTrue(impl.contains(value), "value should be contained");
}
// Clear and make sure size() and isEmpty() match
impl.clear();
assertEquals(0, impl.size(), "size should be 0");
assertTrue(impl.isEmpty(), "collection should be empty");
// Check that values are not in collection
for (Object value : inputs.trim().split(", ")) {
assertFalse(impl.contains(value), "value should not be contained");
}
}
@Order(collectionTestLevel)
@Test
@DisplayName("Test repeated emptying and filling of ICollection with single element")
default void testFillEmptyCollection() {
ICollection<Object> impl = newCollection();
for (int i = 0; i < 10; i ++) {
impl.add("a");
assertEquals(impl.size(), 1, "collection should have 1 element");
impl.clear();
assertTrue(impl.isEmpty());
}
}
@Order(collectionTestLevel)
@DisplayName("Stress test for add(...)")
@ParameterizedTest(name = "Test add()ing {1} random numbers with seed = {0}")
@CsvSource({
"100, 3000", "42, 1000"
})
default void stressTestAdd(int seed, int size) {
Random r = new Random(seed);
List<Integer> reference = new java.util.ArrayList<>();
ICollection<Object> impl = newCollection();
// Test adding values updates size and displays contained correctly
for (int i = 0; i < size; i++) {
int num = r.nextInt();
reference.add(num);
impl.add(num);
assertEquals(reference.size(), impl.size(), "size()s are not equal");
assertEquals(reference.contains(num), impl.contains(num), "value should be contained");
}
// Test that values not in collection are not contained
for (int i = 0; i < size; i++) {
int num = r.nextInt();
assertEquals(reference.contains(num), impl.contains(num), "contained values do not match");
}
}
@Order(collectionTestLevel)
@DisplayName("Stress test for contains(...)")
@ParameterizedTest(name = "Test contains() with {1} random numbers and seed = {0}")
@CsvSource({
"100, 3000", "42, 1000"
})
default void stressTestContains(int seed, int size) {
Random r = new Random(seed);
List<Integer> nums = new java.util.ArrayList<>();
ICollection<Object> impl = newCollection();
// Add values to both the list of nums and test collection
for (int i = 0; i < size; i++) {
int num = r.nextInt();
nums.add(num);
impl.add(num);
}
// Shuffle order of nums and check that all are contained in the collection
Collections.shuffle(nums);
for (int num : nums) {
assertEquals(true, impl.contains(num), "value should be contained");
}
// Test that values not in collection are not contained
for (int i = 0; i < size; i++) {
int num = r.nextInt();
assertEquals(nums.contains(num), impl.contains(num), "contained values do not match");
}
}
}
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.interfaces.ICollection;
import edu.caltech.cs2.interfaces.IDeque;
import org.hamcrest.MatcherAssert;
import org.hamcrest.collection.IsEmptyIterable;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.hamcrest.collection.IsIterableContainingInOrder;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Random;
import static edu.caltech.cs2.project03.Project03TestOrdering.*;
import static org.junit.jupiter.api.Assertions.*;
public interface DequeTests extends CollectionTests {
IDeque<Object> newDeque();
@Order(dequeTestLevel)
@DisplayName("Test Deque Iterator")
@ParameterizedTest(name = "Test deque iterator on [{arguments}]")
@ValueSource(strings = {
"",
"1",
"0, 1, 2, 3",
"5, 4, 3, 2, 1",
"8, 3, 5, 7, 4, 3, 12, 12, 1"
})
default void testIterator(String inputs) {
ArrayDeque<Object> ref = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
for (Object value : inputs.trim().split(", ")) {
impl.addBack(value);
ref.addLast(value);
MatcherAssert.assertThat(impl, IsIterableContainingInOrder.contains(ref.toArray()));
}
for (Object value : inputs.trim().split(", ")) {
MatcherAssert.assertThat(impl, IsIterableContainingInOrder.contains(ref.toArray()));
impl.removeBack();
ref.removeLast();
}
MatcherAssert.assertThat(impl, IsEmptyIterable.emptyIterable());
}
@Test
@Order(dequeTestLevel)
@DisplayName("Test Deque addFront / removeFront edge cases")
default void testRepeatedAddFrontRemoveFront() {
ArrayDeque<Object> ref = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
for (int i = 0; i < 10; i ++) {
for (int j = 0; j < 5; j ++) {
impl.addFront(i);
ref.addFirst(i);
MatcherAssert.assertThat(impl, IsIterableContainingInOrder.contains(ref.toArray()));
}
for (int j = 0; j < 5; j ++) {
MatcherAssert.assertThat(impl, IsIterableContainingInOrder.contains(ref.toArray()));
impl.removeFront();
ref.removeFirst();
}
assertTrue(impl.isEmpty());
MatcherAssert.assertThat(impl, IsEmptyIterable.emptyIterable());
}
}
@Test
@Order(dequeTestLevel)
@DisplayName("Test Deque addFront / removeBack edge cases")
default void testRepeatedAddFrontRemoveBack() {
ArrayDeque<Object> ref = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
for (int i = 0; i < 10; i ++) {
for (int j = 0; j < 5; j ++) {
impl.addFront(i);
ref.addFirst(i);
MatcherAssert.assertThat(impl, IsIterableContainingInOrder.contains(ref.toArray()));
}
for (int j = 0; j < 5; j ++) {
MatcherAssert.assertThat(impl, IsIterableContainingInOrder.contains(ref.toArray()));
impl.removeBack();
ref.removeLast();
}
assertTrue(impl.isEmpty());
MatcherAssert.assertThat(impl, IsEmptyIterable.emptyIterable());
}
}
@Test
@Order(dequeTestLevel)
@DisplayName("Test Deque addBack / removeFront edge cases")
default void testRepeatedAddBackRemoveFront() {
ArrayDeque<Object> ref = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
for (int i = 0; i < 10; i ++) {
for (int j = 0; j < 5; j ++) {
impl.addBack(i);
ref.addLast(i);
MatcherAssert.assertThat(impl, IsIterableContainingInOrder.contains(ref.toArray()));
}
for (int j = 0; j < 5; j ++) {
MatcherAssert.assertThat(impl, IsIterableContainingInOrder.contains(ref.toArray()));
impl.removeFront();
ref.removeFirst();
}
assertTrue(impl.isEmpty());
MatcherAssert.assertThat(impl, IsEmptyIterable.emptyIterable());
}
}
@Test
@Order(dequeTestLevel)
@DisplayName("Test Deque addBack / removeBack edge cases")
default void testRepeatedAddBackRemoveBack() {
ArrayDeque<Object> ref = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
for (int i = 0; i < 10; i ++) {
for (int j = 0; j < 5; j ++) {
impl.addBack(i);
ref.addLast(i);
MatcherAssert.assertThat(impl, IsIterableContainingInOrder.contains(ref.toArray()));
}
for (int j = 0; j < 5; j ++) {
impl.removeBack();
ref.removeLast();
}
assertTrue(impl.isEmpty());
MatcherAssert.assertThat(impl, IsEmptyIterable.emptyIterable());
}
}
@Order(dequeTestLevel)
@DisplayName("Stress test for addFront(...) and peekFront(...)")
@ParameterizedTest(name = "Test addFront()ing {1} random numbers with seed = {0}")
@CsvSource({
"100, 300", "42, 500"
})
default void stressTestAddFront(int seed, int size) {
Random r = new Random(seed);
Deque<Object> reference = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
// Test that first peek is null
assertNull(impl.peekFront(), "empty peek should return null");
// Test adding values updates size and displays contained correctly
for (int i = 0; i < size; i++) {
int num = r.nextInt();
reference.addFirst(num);
impl.addFront(num);
assertEquals(reference.size(), impl.size(), "size()s are not equal");
assertEquals(reference.peekFirst(), impl.peekFront(), "peeks should be the same");
assertEquals(reference.toString(), impl.toString(), "toStrings()s are not equal");
}
}
@Order(dequeTestLevel)
@DisplayName("Stress test for addBack(...) and peekBack(...)")
@ParameterizedTest(name = "Test addBack()ing {1} random numbers with seed = {0}")
@CsvSource({
"100, 300", "42, 500"
})
default void stressTestAddBack(int seed, int size) {
Random r = new Random(seed);
Deque<Object> reference = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
// Test that first peek is null
assertNull(impl.peekBack(), "empty peek should return null");
// Test adding values updates size and displays contained correctly
for (int i = 0; i < size; i++) {
int num = r.nextInt();
reference.addLast(num);
impl.addBack(num);
assertEquals(reference.size(), impl.size(), "size()s are not equal");
assertEquals(reference.peekLast(), impl.peekBack(), "peeks should be the same");
assertEquals(reference.toString(), impl.toString(), "toStrings()s are not equal");
}
}
@Order(dequeTestLevel)
@DisplayName("Stress test for removeFront(...)")
@ParameterizedTest(name = "Test removeFront()ing {1} random numbers with seed = {0}")
@CsvSource({
"101, 300", "45, 500"
})
default void stressTestRemoveFront(int seed, int size) {
Random r = new Random(seed);
Deque<Object> reference = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
// Test that first removeFront is null
assertNull(impl.removeFront(), "empty removeFront should return null");
for (int i = 0; i < size; i++) {
int num = r.nextInt();
reference.addFirst(num);
impl.addFront(num);
assertEquals(reference.peekFirst(), impl.peekFront(),"return values of peekFront()s are not equal");
if (r.nextBoolean()) {
assertEquals(reference.removeFirst(), impl.removeFront(),"return values of removeFront()s are not equal");
assertEquals(reference.peekFirst(), impl.peekFront(),"return values of peekFront()s are not equal");
}
assertEquals(reference.size(), impl.size(), "size()s are not equal");
assertEquals(reference.toString(), impl.toString(), "toStrings()s are not equal");
}
}
@Order(dequeTestLevel)
@DisplayName("Stress test for removeBack(...)")
@ParameterizedTest(name = "Test removeBack()ing {1} random numbers with seed = {0}")
@CsvSource({
"101, 300", "45, 500"
})
default void stressTestRemoveBack(int seed, int size) {
Random r = new Random(seed);
Deque<Object> reference = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
// Test that first removeBack is null
assertNull(impl.removeBack(), "empty removeBack should return null");
for (int i = 0; i < size; i++) {
int num = r.nextInt();
reference.addLast(num);
impl.addBack(num);
assertEquals(reference.peekLast(), impl.peekBack(),"return values of peekBack()s are not equal");
if (r.nextBoolean()) {
assertEquals(reference.removeLast(), impl.removeBack(),"return values of removeBack()s are not equal");
assertEquals(reference.peekLast(), impl.peekBack(),"return values of peekBack()s are not equal");
}
assertEquals(reference.size(), impl.size(), "size()s are not equal");
assertEquals(reference.toString(), impl.toString(), "toStrings()s are not equal");
}
}
@Order(dequeTestLevel)
@DisplayName("Stress test full IDeque")
@ParameterizedTest(name = "Test all IDeque methods {1} random numbers with seed = {0}")
@CsvSource({
"102, 300", "52, 500"
})
default void stressTestFullDeque(int seed, int size) {
Random r = new Random(seed);
Deque<Object> reference = new ArrayDeque<>();
IDeque<Object> impl = newDeque();
for (int i = 0; i < size; i++) {
int num = r.nextInt();
// Add to either front or back
if (r.nextBoolean()) {
reference.addFirst(num);
impl.addFront(num);
}
else {
reference.addLast(num);
impl.addBack(num);
}
// Test that peeks are correct
assertEquals(reference.peekFirst(), impl.peekFront(),"return values of peekFront()s are not equal");
assertEquals(reference.peekLast(), impl.peekBack(),"return values of peekBacks()s are not equal");
// If true, remove an element
if (r.nextBoolean()) {
// If true, remove from front, else remove from back
if (r.nextBoolean()) {
assertEquals(reference.removeFirst(), impl.removeFront(),"return values of removeFront()s are not equal");
}
else {
assertEquals(reference.removeLast(), impl.removeBack(),"return values of removeBack()s are not equal");
}
assertEquals(reference.peekFirst(), impl.peekFront(),"return values of peekFront()s are not equal");
assertEquals(reference.peekLast(), impl.peekBack(),"return values of peekBacks()s are not equal");
}
assertEquals(reference.size(), impl.size(), "size()s are not equal");
assertEquals(reference.toString(), impl.toString(), "toStrings()s are not equal");
}
}
@Order(dequeTestLevel)
@DisplayName("Test for addAll(...)")
@ParameterizedTest(name = "Test addAll with {1} random numbers and seed = {0}")
@CsvSource({
"99, 300", "48, 500"
})
default void testAddAll(int seed, int size) {
Random r = new Random(seed);
ICollection<Object> coll = newDeque();
IDeque<Object> impl = newDeque();
for (int i = 0; i < size; i++) {
int num = r.nextInt();
coll.add(num);
}
impl.addAll(coll);
for (Object num : coll) {
assertTrue(impl.contains(num), "value should be contained in Deque");
}
}
}
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.helpers.Reflection;
import edu.caltech.cs2.interfaces.IFixedSizeQueue;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import java.lang.reflect.Constructor;
import java.util.Queue;
import java.util.Random;
import static edu.caltech.cs2.project03.Project03TestOrdering.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
public interface FixedSizeQueueTests extends QueueTests {
IFixedSizeQueue<Object> newFixedSizeQueue(int capacity);
@Order(fixedSizeQueueLevel)
@DisplayName("Overflow test for enqueue(...)")
@ParameterizedTest(name = "Test randomly enqueue()ing/dequeue()ing {1} random numbers with seed = {0} and fixed array size = {2}")
@CsvSource({
"97, 3000, 100", "38, 5000, 10"
})
default void overflowTestEnqueue(int seed, int numVals, int queueSize) {
Random r = new Random(seed);
Constructor c = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
IFixedSizeQueue<Object> me = newFixedSizeQueue(queueSize);
Queue<Object> reference = new java.util.ArrayDeque<>();
assertEquals(queueSize, me.capacity(), "capacity does not match expected value");
int count = 0;
for (int i = 0; i < numVals; i++) {
int num = r.nextInt();
// Check that we get the expected value from enqueue when it has a risk of overflowing
if (count < queueSize) {
assertEquals(false, me.isFull(), "queue should not be full");
assertEquals(true, me.enqueue(num), "enqueue should be successful");
reference.add(num);
count++;
}
else {
assertEquals(true, me.isFull(), "queue should be full");
assertEquals(false, me.enqueue(num), "enqueue should have failed");
}
// Standard checks to make sure peeks() and dequeues() match up
assertEquals(reference.peek(), me.peek(),"return values of peek()s are not equal");
if (r.nextBoolean()) {
assertEquals(reference.remove(), me.dequeue(),"return values of dequeue()s are not equal");
assertEquals(reference.peek(), me.peek(),"return values of peek()s are not equal");
count--;
}
assertEquals(reference.size(), me.size(), "size()s are not equal");
assertEquals(queueSize, me.capacity(), "capacity does not match expected value");
}
}
}
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.helpers.Inspection;
import edu.caltech.cs2.helpers.Reflection;
import edu.caltech.cs2.helpers.RuntimeInstrumentation;
import edu.caltech.cs2.interfaces.ICollection;
import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.IQueue;
import edu.caltech.cs2.interfaces.IStack;
import edu.caltech.cs2.project03.Project03TestOrdering.*;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import static edu.caltech.cs2.project03.Project03TestOrdering.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Tag("C")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LinkedDequeTests implements DequeTests, StackTests, QueueTests {
private static String LINKED_DEQUE_SOURCE ="src/edu/caltech/cs2/datastructures/LinkedDeque.java";
private Constructor linkedDequeConstructor = Reflection.getConstructor(LinkedDeque.class);
public ICollection<Object> newCollection() {
return Reflection.newInstance(linkedDequeConstructor);
}
public IDeque<Object> newDeque() {
return Reflection.newInstance(linkedDequeConstructor);
}
public IStack<Object> newStack() {
return Reflection.newInstance(linkedDequeConstructor);
}
public IQueue<Object> newQueue() {
return Reflection.newInstance(linkedDequeConstructor);
}
public IQueue<Object> newQueue(int size) {
return newQueue();
}
// LINKEDDEQUE-SPECIFIC TESTS ----------------------------------------
@Order(classSpecificTestLevel)
@DisplayName("Does not use or import disallowed classes")
@Test
public void testForInvalidClasses() {
List<String> regexps = List.of("java\\.util\\.(?!Iterator)", "java\\.lang\\.reflect", "java\\.io");
Inspection.assertNoImportsOf(LINKED_DEQUE_SOURCE, regexps);
Inspection.assertNoUsageOf(LINKED_DEQUE_SOURCE, regexps);
}
@Order(classSpecificTestLevel)
@DisplayName("There are no static fields")
@Test
public void testConstantFields() {
Reflection.assertFieldsEqualTo(LinkedDeque.class, "static", 0);
}
@Order(classSpecificTestLevel)
@DisplayName("The overall number of fields is small")
@Test
public void testSmallNumberOfFields() {
Reflection.assertFieldsLessThan(LinkedDeque.class, "private", 4);
}
@Order(classSpecificTestLevel)
@DisplayName("There are no public fields")
@Test
public void testNoPublicFields() {
Reflection.assertNoPublicFields(LinkedDeque.class);
}
@Order(classSpecificTestLevel)
@DisplayName("The public interface is correct")
@Test
public void testPublicInterface() {
Reflection.assertPublicInterface(LinkedDeque.class, List.of(
"addFront",
"addBack",
"removeFront",
"removeBack",
"enqueue",
"dequeue",
"push",
"pop",
"peek",
"peekFront",
"peekBack",
"iterator",
"size",
"toString"
));
}
@Order(classSpecificTestLevel)
@DisplayName("Uses this(...) notation in all but one constructor")
@Test
public void testForThisConstructors() {
Inspection.assertConstructorHygiene(LINKED_DEQUE_SOURCE);
}
// TOSTRING TESTS ---------------------------------------------------
@Order(toStringTestLevel)
@DisplayName("toString is correctly overridden")
@Test
public void testToStringOverride() {
Reflection.assertMethodCorrectlyOverridden(LinkedDeque.class, "toString");
}
@Order(toStringTestLevel)
@DisplayName("toString() matches java.util.ArrayDeque")
@ParameterizedTest(name = "Test toString() on [{arguments}]")
@ValueSource(strings = {
"0, 1, 2, 3", "5, 4, 3, 2, 1", "8, 3, 5, 7, 4, 3, 12, 12, 1"
})
public void testToString(String inputs) {
java.util.ArrayDeque<String> reference = new java.util.ArrayDeque<String>();
LinkedDeque<String> me = new LinkedDeque<>();
for (String value : inputs.trim().split(", ")) {
assertEquals(reference.toString(), me.toString(), "toString outputs should be the same");
reference.addLast(value);
me.addBack(value);
}
}
// TIME COMPLEXITY TESTS ------------------------------------------------
@Order(complexityTestLevel)
@DisplayName("addFront() and removeFront() take constant time")
@Test
public void testFrontDequeOperationComplexity() {
Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
IDeque<Integer> q = new LinkedDeque<>();
for (int i = 0; i < numElements; i++) {
q.addFront(i);
}
return q;
};
Consumer<IDeque<Integer>> addFront = (IDeque<Integer> q) -> q.addFront(0);
Consumer<IDeque<Integer>> removeFront = (IDeque<Integer> q) -> q.removeFront();
RuntimeInstrumentation.assertAtMost("addFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, addFront, 8);
RuntimeInstrumentation.assertAtMost("removeFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, removeFront, 8);
}
@Order(complexityTestLevel)
@DisplayName("addBack() and removeBack() take constant time")
@Test
public void testBackDequeOperationComplexity() {
Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
IDeque<Integer> q = new LinkedDeque<>();
for (int i = 0; i < numElements; i++) {
q.addBack(i);
}
return q;
};
Consumer<IDeque<Integer>> addBack = (IDeque<Integer> q) -> q.addBack(0);
Consumer<IDeque<Integer>> removeBack = (IDeque<Integer> q) -> q.removeBack();
RuntimeInstrumentation.assertAtMost("addBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, addBack, 8);
RuntimeInstrumentation.assertAtMost("removeBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, removeBack, 8);
}
@Order(complexityTestLevel)
@DisplayName("enqueue() and dequeue() take constant time")
@Test
public void testQueueOperationComplexity() {
Function<Integer, IQueue<Integer>> provide = (Integer numElements) -> {
IQueue<Integer> q = new LinkedDeque<>();
for (int i = 0; i < numElements; i++) {
q.enqueue(i);
}
return q;
};
Consumer<IQueue<Integer>> enqueue = (IQueue<Integer> q) -> q.enqueue(0);
Consumer<IQueue<Integer>> dequeue = (IQueue<Integer> q) -> q.dequeue();
RuntimeInstrumentation.assertAtMost("enqueue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, enqueue, 8);
RuntimeInstrumentation.assertAtMost("dequeue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, dequeue, 8);
}
@Order(complexityTestLevel)
@DisplayName("push() and pop() take constant time")
@Test
public void testStackOperationComplexity() {
Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
IStack<Integer> q = new LinkedDeque<>();
for (int i = 0; i < numElements; i++) {
q.push(i);
}
return q;
};
Consumer<IStack<Integer>> push = (IStack<Integer> q) -> q.push(0);
Consumer<IStack<Integer>> pop = (IStack<Integer> q) -> q.pop();
RuntimeInstrumentation.assertAtMost("push", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, push, 8);
RuntimeInstrumentation.assertAtMost("pop", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, pop, 8);
}
@Order(complexityTestLevel)
@DisplayName("peek() takes constant time")
@Test
public void testPeekComplexity() {
Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
IStack<Integer> q = new LinkedDeque<>();
for (int i = 0; i < numElements; i++) {
q.push(i);
}
return q;
};
Consumer<IStack<Integer>> peek = (IStack<Integer> q) -> q.peek();
RuntimeInstrumentation.assertAtMost("peek", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peek, 8);
}
@Order(complexityTestLevel)
@DisplayName("peekFront() takes constant time")
@Test
public void testPeekFrontComplexity() {
Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
IDeque<Integer> q = new LinkedDeque<>();
for (int i = 0; i < numElements; i++) {
q.addFront(i);
}
return q;
};
Consumer<IDeque<Integer>> peekFront = (IDeque<Integer> q) -> q.peekFront();
RuntimeInstrumentation.assertAtMost("peekFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekFront, 8);
}
@Order(complexityTestLevel)
@DisplayName("peekBack() takes constant time")
@Test
public void testPeekBackComplexity() {
Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
IDeque<Integer> q = new LinkedDeque<>();
for (int i = 0; i < numElements; i++) {
q.addBack(i);
}
return q;
};
Consumer<IDeque<Integer>> peekBack = (IDeque<Integer> q) -> q.peekBack();
RuntimeInstrumentation.assertAtMost("peekBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekBack, 8);
}
}
\ No newline at end of file
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.interfaces.IQueue;
import edu.caltech.cs2.interfaces.IStack;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import java.util.Queue;
import java.util.Random;
import static edu.caltech.cs2.project03.Project03TestOrdering.*;
import static org.junit.jupiter.api.Assertions.*;
public interface QueueTests {
IQueue<Object> newQueue();
IQueue<Object> newQueue(int size);
@Order(queueTestLevel)
@DisplayName("Stress test for enqueue(...) and peek(...)")
@ParameterizedTest(name = "Test enqueue()ing {1} random numbers with seed = {0}")
@CsvSource({
"97, 3000", "38, 5000"
})
default void stressTestEnqueue(int seed, int size) {
Random r = new Random(seed);
Queue<Object> reference = new java.util.ArrayDeque<>();
IQueue<Object> me = newQueue(size);
// Test that first peek is null
assertNull(me.peek(), "empty peek should return null");
// Test adding values updates size and peek correctly
for (int i = 0; i < size; i++) {
int num = r.nextInt();
reference.add(num);
assertTrue(me.enqueue(num), "enqueue should be successful");
assertEquals(reference.size(), me.size(), "size()s are not equal");
assertEquals(reference.peek(), me.peek(), "peeks should be the same");
}
}
@Order(queueTestLevel)
@DisplayName("Stress test for dequeue(...)")
@ParameterizedTest(name = "Test dequeue()ing {1} random numbers with seed = {0}")
@CsvSource({
"98, 3000", "39, 5000"
})
default void stressTestDequeue(int seed, int size) {
Random r = new Random(seed);
Queue<Object> reference = new java.util.ArrayDeque<>();
IQueue<Object> me = newQueue(size);
// Test that first dequeue is null
assertNull(me.dequeue(), "empty dequeue should return null");
for (int i = 0; i < size; i++) {
int num = r.nextInt();
reference.add(num);
assertTrue(me.enqueue(num), "enqueue should be successful");
assertEquals(reference.peek(), me.peek(),"return values of peek()s are not equal");
if (r.nextBoolean()) {
assertEquals(reference.remove(), me.dequeue(),"return values of dequeue()s are not equal");
assertEquals(reference.peek(), me.peek(),"return values of peek()s are not equal");
}
assertEquals(reference.size(), me.size(), "size()s are not equal");
}
}
}
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.interfaces.IStack;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import java.util.Deque;
import java.util.Random;
import static edu.caltech.cs2.project03.Project03TestOrdering.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
public interface StackTests {
IStack<Object> newStack();
@Order(stackTestLevel)
@DisplayName("Stress test for push(...) and peek(...)")
@ParameterizedTest(name = "Test push()ing {1} random numbers with seed = {0}")
@CsvSource({
"99, 3000", "40, 5000"
})
default void stressTestPush(int seed, int size) {
Random r = new Random(seed);
Deque<Object> reference = new java.util.ArrayDeque<>();
IStack<Object> me = newStack();
// Test that first peek is null
assertNull(me.peek(), "empty peek should return null");
// Test adding values updates size and peek correctly
for (int i = 0; i < size; i++) {
int num = r.nextInt();
reference.push(num);
me.push(num);
assertEquals(reference.size(), me.size(), "size()s are not equal");
assertEquals(reference.peek(), me.peek(), "peeks should be the same");
}
}
@Order(stackTestLevel)
@DisplayName("Stress test for pop(...)")
@ParameterizedTest(name = "Test pop()ing {1} random numbers with seed = {0}")
@CsvSource({
"98, 3000", "39, 5000"
})
default void stressTestPop(int seed, int size) {
Random r = new Random(seed);
Deque<Object> reference = new java.util.ArrayDeque<>();
IStack<Object> me = newStack();
// Test that first pop is null
assertNull(me.pop(), "empty pop should return null");
for (int i = 0; i < size; i++) {
int num = r.nextInt();
reference.push(num);
me.push(num);
assertEquals(reference.peek(), me.peek(),"return values of peek()s are not equal");
if (r.nextBoolean()) {
assertEquals(reference.pop(), me.pop(),"return values of pop()s are not equal");
assertEquals(reference.peek(), me.peek(),"return values of peek()s are not equal");
}
assertEquals(reference.size(), me.size(), "size()s are not equal");
}
}
}
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.caltech.cs2.helpers;
import org.hamcrest.Matcher;
import org.junit.jupiter.api.extension.*;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.junit.platform.commons.support.ReflectionSupport;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
/**
* {@code @CaptureSystemOutput} is a JUnit JUpiter extension for capturing
* output to {@code System.out} and {@code System.err} with expectations
* supported via Hamcrest matchers.
*
* <h4>Example Usage</h4>
*
* <pre style="code">
* {@literal @}Test
* {@literal @}CaptureSystemOutput
* void systemOut(OutputCapture outputCapture) {
* outputCapture.expect(containsString("System.out!"));
*
* System.out.println("Printed to System.out!");
* }
*
* {@literal @}Test
* {@literal @}CaptureSystemOutput
* void systemErr(OutputCapture outputCapture) {
* outputCapture.expect(containsString("System.err!"));
*
* System.err.println("Printed to System.err!");
* }
* </pre>
*
* <p>Based on code from Spring Boot's
* <a href="https://github.com/spring-projects/spring-boot/blob/d3c34ee3d1bfd3db4a98678c524e145ef9bca51c/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/rule/OutputCapture.java">OutputCapture</a>
* rule for JUnit 4 by Phillip Webb and Andy Wilkinson.
*
* @author Sam Brannen
* @author Phillip Webb
* @author Andy Wilkinson
*/
@Target({TYPE, METHOD})
@Retention(RUNTIME)
@ExtendWith(CaptureSystemOutput.Extension.class)
public @interface CaptureSystemOutput {
class Extension implements BeforeEachCallback, AfterEachCallback, ParameterResolver {
@Override
public void beforeEach(ExtensionContext context) throws Exception {
getOutputCapture(context).captureOutput();
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
OutputCapture outputCapture = getOutputCapture(context);
try {
if (!outputCapture.matchers.isEmpty()) {
String output = outputCapture.toString();
assertThat(output, allOf(outputCapture.matchers));
}
} finally {
outputCapture.releaseOutput();
}
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
boolean isTestMethodLevel = extensionContext.getTestMethod().isPresent();
boolean isOutputCapture = parameterContext.getParameter().getType() == OutputCapture.class;
return isTestMethodLevel && isOutputCapture;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return getOutputCapture(extensionContext);
}
private OutputCapture getOutputCapture(ExtensionContext context) {
return getOrComputeIfAbsent(getStore(context), OutputCapture.class);
}
private <V> V getOrComputeIfAbsent(Store store, Class<V> type) {
return store.getOrComputeIfAbsent(type, ReflectionSupport::newInstance, type);
}
private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod()));
}
}
/**
* {@code OutputCapture} captures output to {@code System.out} and {@code System.err}.
*
* <p>To obtain an instance of {@code OutputCapture}, declare a parameter of type
* {@code OutputCapture} in a JUnit Jupiter {@code @Test}, {@code @BeforeEach},
* or {@code @AfterEach} method.
*
* <p>{@linkplain #expect Expectations} are supported via Hamcrest matchers.
*
* <p>To obtain all output to {@code System.out} and {@code System.err}, simply
* invoke {@link #toString()}.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author Sam Brannen
*/
static class OutputCapture {
private final List<Matcher<? super String>> matchers = new ArrayList<>();
private CaptureOutputStream captureOut;
private CaptureOutputStream captureErr;
private ByteArrayOutputStream copy;
void captureOutput() {
this.copy = new ByteArrayOutputStream();
this.captureOut = new CaptureOutputStream(System.out, this.copy);
this.captureErr = new CaptureOutputStream(System.err, this.copy);
System.setOut(new PrintStream(this.captureOut));
System.setErr(new PrintStream(this.captureErr));
}
void releaseOutput() {
System.setOut(this.captureOut.getOriginal());
System.setErr(this.captureErr.getOriginal());
this.copy = null;
}
private void flush() {
try {
this.captureOut.flush();
this.captureErr.flush();
} catch (IOException ex) {
// ignore
}
}
/**
* Verify that the captured output is matched by the supplied {@code matcher}.
*
* <p>Verification is performed after the test method has executed.
*
* @param matcher the matcher
*/
public void expect(Matcher<? super String> matcher) {
this.matchers.add(matcher);
}
/**
* Return all captured output to {@code System.out} and {@code System.err}
* as a single string.
*/
@Override
public String toString() {
flush();
return this.copy.toString();
}
private static class CaptureOutputStream extends OutputStream {
private final PrintStream original;
private final OutputStream copy;
CaptureOutputStream(PrintStream original, OutputStream copy) {
this.original = original;
this.copy = copy;
}
PrintStream getOriginal() {
return this.original;
}
@Override
public void write(int b) throws IOException {
this.copy.write(b);
//this.original.write(b);
//this.original.flush();
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
this.copy.write(b, off, len);
//this.original.write(b, off, len);
}
@Override
public void flush() throws IOException {
this.copy.flush();
this.original.flush();
}
}
}
}
package edu.caltech.cs2.helpers;
import org.junit.jupiter.params.provider.ArgumentsSource;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(FileSourceProvider.class)
public @interface FileSource {
String[] inputs();
String[] outputFiles();
}
package edu.caltech.cs2.helpers;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.support.AnnotationConsumer;
import org.junit.platform.commons.util.Preconditions;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.fail;
public class FileSourceProvider implements ArgumentsProvider, AnnotationConsumer<FileSource> {
private String[] inputs;
private String[] outputFiles;
@Override
public void accept(FileSource source) {
this.inputs = source.inputs();//Stream.of(source.inputs()).map((x) -> Arrays.asList(x.split("\\|"))).collect(Collectors.toList());
this.outputFiles = source.outputFiles();
}
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
Arguments[] args = new Arguments[this.outputFiles.length];
for (int i = 0; i < this.outputFiles.length; i++) {
String inputArgs = this.inputs[i];
Scanner in = getScanner(this.outputFiles[i]);
String output = in.useDelimiter("\\Z").next();
args[i] = Arguments.arguments(inputArgs, output);
}
return Stream.of(args);
}
private Scanner getScanner(String resource) {
Preconditions.notBlank(resource, "Test file " + resource + " must not be null or blank");
try {
return new Scanner(new File("tests/data/" + resource));
} catch (FileNotFoundException e) {
fail("Test file " + resource + " does not exist");
}
return null;
}
}
package edu.caltech.cs2.helpers;
import org.junit.jupiter.params.provider.ArgumentsSource;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(ImageFileSourceProvider.class)
public @interface ImageFileSource {
String[] inputs();
String[] outputFiles();
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment