Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
cs2-20wi
project03
Commits
d476add1
Commit
d476add1
authored
5 years ago
by
Adam Blank
Browse files
Options
Download
Email Patches
Plain Diff
Initial Commit
parents
master
No related merge requests found
Pipeline
#36403
canceled with stage
Changes
47
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
src/edu/caltech/cs2/interfaces/IStack.java
+33
-0
src/edu/caltech/cs2/interfaces/IStack.java
src/edu/caltech/cs2/project03/CircularArrayFixedSizeQueueGuitarString.java
+29
-0
...s2/project03/CircularArrayFixedSizeQueueGuitarString.java
src/edu/caltech/cs2/project03/GuitarHero.java
+46
-0
src/edu/caltech/cs2/project03/GuitarHero.java
src/edu/caltech/cs2/project03/libraries/StdAudio.java
+428
-0
src/edu/caltech/cs2/project03/libraries/StdAudio.java
src/edu/caltech/cs2/project03/libraries/StdDraw.java
+1924
-0
src/edu/caltech/cs2/project03/libraries/StdDraw.java
tests/data/ticStates1.txt
+102
-0
tests/data/ticStates1.txt
tests/data/ticStates2.txt
+98
-0
tests/data/ticStates2.txt
tests/data/ticStates3.txt
+101
-0
tests/data/ticStates3.txt
tests/edu/caltech/cs2/datastructures/ArrayDequeTests.java
+266
-0
tests/edu/caltech/cs2/datastructures/ArrayDequeTests.java
tests/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueueTests.java
+162
-0
.../cs2/datastructures/CircularArrayFixedSizeQueueTests.java
tests/edu/caltech/cs2/datastructures/CollectionTests.java
+136
-0
tests/edu/caltech/cs2/datastructures/CollectionTests.java
tests/edu/caltech/cs2/datastructures/DequeTests.java
+298
-0
tests/edu/caltech/cs2/datastructures/DequeTests.java
tests/edu/caltech/cs2/datastructures/FixedSizeQueueTests.java
+59
-0
...s/edu/caltech/cs2/datastructures/FixedSizeQueueTests.java
tests/edu/caltech/cs2/datastructures/LinkedDequeTests.java
+261
-0
tests/edu/caltech/cs2/datastructures/LinkedDequeTests.java
tests/edu/caltech/cs2/datastructures/QueueTests.java
+67
-0
tests/edu/caltech/cs2/datastructures/QueueTests.java
tests/edu/caltech/cs2/datastructures/StackTests.java
+65
-0
tests/edu/caltech/cs2/datastructures/StackTests.java
tests/edu/caltech/cs2/helpers/CaptureSystemOutput.java
+237
-0
tests/edu/caltech/cs2/helpers/CaptureSystemOutput.java
tests/edu/caltech/cs2/helpers/FileSource.java
+17
-0
tests/edu/caltech/cs2/helpers/FileSource.java
tests/edu/caltech/cs2/helpers/FileSourceProvider.java
+49
-0
tests/edu/caltech/cs2/helpers/FileSourceProvider.java
tests/edu/caltech/cs2/helpers/ImageFileSource.java
+17
-0
tests/edu/caltech/cs2/helpers/ImageFileSource.java
with
4395 additions
and
0 deletions
+4395
-0
src/edu/caltech/cs2/interfaces/IStack.java
0 → 100644
View file @
d476add1
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
();
}
This diff is collapsed.
Click to expand it.
src/edu/caltech/cs2/project03/CircularArrayFixedSizeQueueGuitarString.java
0 → 100644
View file @
d476add1
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
;
}
}
This diff is collapsed.
Click to expand it.
src/edu/caltech/cs2/project03/GuitarHero.java
0 → 100644
View file @
d476add1
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
();
}
}
}
}
This diff is collapsed.
Click to expand it.
src/edu/caltech/cs2/project03/libraries/StdAudio.java
0 → 100644
View file @
d476add1
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
();
}
}
This diff is collapsed.
Click to expand it.
src/edu/caltech/cs2/project03/libraries/StdDraw.java
0 → 100644
View file @
d476add1
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, –∞) to (0.5, ∞) 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
This diff is collapsed.
Click to expand it.
tests/data/ticStates1.txt
0 → 100644
View file @
d476add1
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
This diff is collapsed.
Click to expand it.
tests/data/ticStates2.txt
0 → 100644
View file @
d476add1
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
This diff is collapsed.
Click to expand it.
tests/data/ticStates3.txt
0 → 100644
View file @
d476add1
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
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/datastructures/ArrayDequeTests.java
0 → 100644
View file @
d476add1
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
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueueTests.java
0 → 100644
View file @
d476add1
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
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/datastructures/CollectionTests.java
0 → 100644
View file @
d476add1
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"
);
}
}
}
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/datastructures/DequeTests.java
0 → 100644
View file @
d476add1
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"
);
}
}
}
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/datastructures/FixedSizeQueueTests.java
0 → 100644
View file @
d476add1
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"
);
}
}
}
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/datastructures/LinkedDequeTests.java
0 → 100644
View file @
d476add1
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
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/datastructures/QueueTests.java
0 → 100644
View file @
d476add1
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"
);
}
}
}
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/datastructures/StackTests.java
0 → 100644
View file @
d476add1
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"
);
}
}
}
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/helpers/CaptureSystemOutput.java
0 → 100644
View file @
d476add1
/*
* 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
();
}
}
}
}
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/helpers/FileSource.java
0 → 100644
View file @
d476add1
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
();
}
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/helpers/FileSourceProvider.java
0 → 100644
View file @
d476add1
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
;
}
}
This diff is collapsed.
Click to expand it.
tests/edu/caltech/cs2/helpers/ImageFileSource.java
0 → 100644
View file @
d476add1
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
();
}
This diff is collapsed.
Click to expand it.
Prev
1
2
3
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help