Spring 2023
In this lab, you will write a program to read an undirected, unweighted graph from a text file into an adjacency matrix and output the number of connected components in the graph.
The Github Classroom link for Lab 6 is available in the Lab 6 assignment on Canvas. There is no skeleton code, so your repository will start out empty. You will begin by initializing a gradle project in your repo, then you will implement a program to count connected components. Whether or not you finish during lab, make sure to commit and push whatever code you have at the end of the lab period to receive credit for attending lab.
In past assignments and labs, we’ve given you a skeleton repository with gradle all set up to run and test the code. In this lab, you’ll set up a gradle project from scratch.
Start by cloning your (empty) repository.
Change into the repo directory and run gradle init
.
You will be prompted to answer some questions about the project you’d
like to create:
application
for project type. This will make a
project with a run
task that executes some class’s
main
method.Java
for implementation language. This tells
Gradle that this is a Java project.no
when asked whether to split functionality
across multiple subprojects. This prevents the project structure from
being (more) unnecessarily complicated.groovy
for DSL; specifies that the configuration
file build.gradle
will be written in the Groovy language,
which is the default and what we’ve been using this quarter.no
. Let’s stick with the stable and predictable for
now.JUnit 4
for test framework. Although you won’t
be required to write any tests, this sets up a test
task
that compiles and runs JUnit tests stored in the test source
directory.lab6
lab6
also for the source package; this means your
code lives in the lab6 package (i.e., has a package lab6;
declaration at the top of each code file.Take a look around your project directory - the init task has created a bunch of stuff that should look familiar by now. To finish setting up your project like past labs and assignments we need to do a few more things, detailed in the next few steps.
For Java Application projects, gradle defaults to creating a
class called App, stored in
app/src/main/java/lab6/App.java
. Rename this file to be
called Components.java
. Edit the file and replace the
App
class name with Components
(and change the
main method to create an instance of Components
instead of
App
).
Likewise, Gradle auto-generates an JUnit test class in
app/src/test/java/lab6/AppTest.java
. We won’t be asking you
to write any JUnit tests here, so you can delete this file if you’d
like. If you wanted to write some unit tests, you’d want to rename the
file to ComponentsTest.java
and write test cases in
there.
The last thing we need to get something running is to tell Gradle
that the name of the class whose main method we want to run with the
run
task is Components instead of App. Edit the last
section of app/build.gradle
to set
mainClassName
to lab6.Components
.
At this point, try the gradle run
task. You should
see Hello world printed under the :run task.
When running programs with command-line input or output, it’s
often annoying to have gradle’s progress bars and output get in the way.
To make some of this go away, create a file
gradle.properties
in the app
directory and
include the following line:
org.gradle.console=plain
Try gradle run
again and you’ll see the slightly cleaner
output.
You can also control the amount of output (the log level) using
command-line flags, such as -q
for quiet (no output),
-i
for info (lots of output), and -d
for debug
(more output than you know what to do with). If you just want to see
Hello World!
and nothing else, you can
gradle run -q
.
Now seems like a great time to commit your changes to git!
Because the repo started out empty, we need to add all the files to our
repository. Be sure you git add
the following to your
repo:
app/build.gradle
app/gradle.properties
settings.gradle
.gitignore
(this file tells git that you don’t want
to add the build directory and the hidden .gradle directory where gradle
caches local state)
app/src/main/java/lab6/Components.java
Gradle init also generated a wrapper, which allows your
gradle tasks to be run on systems that don’t have gradle installed. On
linux, you use the gradlew
script and on Windows you’d use
gradlew.bat
. These scripts make use of the .jar file in the
gradle
directory to run tasks without help from a local
gradle installation. It’s standard practice to include the necessary
wrapper files in your git repository, so also git add
the
following:
gradlew
gradlew.bat
gradle/wrapper/gradle-wrapper.jar
gradle/wrapper/gradle-wrapper.properties
Here are a couple of other noteworthy changes that we’ve used in
build.gradle
for past assignments that are not relevant to
this lab, but you may find helpful to know about:
If you want your program to accept user input from the command line, you need to tell gradle about this by adding something like the following to build.gradle:
run {
standardInput = System.in
}
Adding the following lines will change the output when running tests to display anything printed by your program or test code, and also print the full stack trace for each test case failure:
test {
testLogging {
showStandardStreams = true;
exceptionFormat 'full'
}
}
A great deal of additional customization is possible by
specifying your preferences in build.gradle
. You can learn
more from the gradle documentation at http://docs.gradle.org.
Implement your program in the Components class. The program takes a single command line argument specifying a filename. The given file adheres to the format specified below. The program should: read the file and create an adjacency matrix representation of the graph, then compute and print the number of connected components in the graph.
You are not required to implement error checking, e.g., for malformed input files or bad command line arguments.
Nodes in this undirected graph are numbered sequentially from \(0..|V|\). The first line of the file contains a single integer, specifying the number of \(|V|\), and each remaining line in the file contains two integers indicating that an edge exists between the two nodes.
Here’s an example of the program’s behavior. The graph drawing is only for illustrative purposes - your program does not need to parse or produce drawings.
Graph drawing: input.txt: Sample invocation:
0 4 5 $ gradle run -q --args "input.txt"
| | 0 1 2
1 - 2 3 3 4 $
1 2
Note that $
denotes the shell prompt, indicating that
the program has printed 2
followed by a newline and then
terminated.
In this lab, you are given no skeleton code and less specific guidance than usual. First focus on parsing input correctly; then get a correct implementation of the core algorithm; then use the algorithm to find connected components. Leave efficiency considerations until last: 8/10 points are based on correctness. Please comment your code well: if we cannot understand your implementation, we can’t give full credit for it.
Be sure that all the requisite files listed in Section 3 are included in your repository. Then, simply commit your final changes and push to GitHub as usual.
This lab is worth 10 points:
Possible deductions: