This commit is contained in:
2026-03-18 00:39:35 -07:00
commit b4fdc98f10
43 changed files with 85012 additions and 0 deletions

41
Bomb.java Normal file
View File

@@ -0,0 +1,41 @@
import java.io.*;
public class Bomb {
public static final int START = 124;
public static final int CODE_LENGTH = 6;
public static void defuse(String code) throws Exception {
if (code.length() != CODE_LENGTH) {
throw new Bomb.Explosion();
}
String curr = Integer.toHexString(CODE_LENGTH);
for (int i = 1; i < CODE_LENGTH; i++) {
curr = scramble(curr);
if (curr.charAt(1) != code.charAt(i)) {
throw new Bomb.Explosion();
}
}
}
private static String scramble(String secret) {
return Integer.toHexString(Integer.parseInt(secret, 16) * 0xA9 / 0b010011);
}
public static void main(String[] args) throws Exception {
System.out.println();
System.out.println("Bomb armed and ready for defusal.");
System.setOut(new PrintStream(new OutputStream() { public void write(int b) {} }));
// defuse("21d74"); // TODO: Find the code, hurry!
defuse("000000");
System.setOut(SYSTEM_OUT);
System.out.println("Bomb defused! You saved the day!");
System.out.println();
}
public static final PrintStream SYSTEM_OUT = System.out;
private static class Explosion extends RuntimeException {
public Explosion() { super("BOOM"); }
}
}

View File

@@ -0,0 +1,47 @@
import java.util.*;
/**
* A strategy game where all players have perfect information and no theme
* or narrative around gameplay.
*/
public abstract class AbstractStrategyGame {
/**
* Constructs and returns a String describing how to play the game. Should include
* any relevant details on how to interpret the game state as returned by toString(),
* how to make moves, the game end condition, and how to win.
*/
public abstract String instructions();
/**
* Constructs and returns a String representation of the current game state.
* This representation should contain all information that should be known to
* players at any point in the game, including board state (if any) and scores (if any).
*/
public abstract String toString();
/**
* Returns true if the game has ended, and false otherwise.
*/
public boolean isGameOver() {
return getWinner() != -1;
}
/**
* Returns the index of the player who has won the game,
* or -1 if the game is not over.
*/
public abstract int getWinner();
/**
* Returns the index of the player who will take the next turn.
* If the game is over, returns -1.
*/
public abstract int getNextPlayer();
/**
* Takes input from the parameter to specify the move the player
* with the next turn wishes to make, then executes that move.
* If any part of the move is illegal, throws an IllegalArgumentException.
*/
public abstract void makeMove(Scanner input);
}

36
c1/Client.java Normal file
View File

@@ -0,0 +1,36 @@
import java.util.*;
public class Client {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
AbstractStrategyGame game = new ConnectFour("dude", "bro");
System.out.println(game.instructions());
System.out.println();
while (!game.isGameOver()) {
System.out.println(game);
System.out.printf("Player %d's turn.\n", game.getNextPlayer());
try {
game.makeMove(console);
} catch (IllegalArgumentException ex) {
System.out.println("**Illegal move: " + ex.getMessage());
}
/**
* Note - the above structure is a try/catch, which is something
* we've included to help deal with the IllegalArgumentExceptions
* in your abstract strategy game!
* We want to remind you that try/catch is a forbidden feature in 123,
* so you SHOULD NOT INCLUDE IT in any code you submit (other than this file)!
* Please see the Code Quality Guide for more info on this.
*/
}
System.out.println(game);
int winner = game.getWinner();
if (winner > 0) {
System.out.printf("Player %d wins!\n", winner);
} else {
System.out.println("It's a tie!");
}
}
}

117
c1/ConnectFour.java Normal file
View File

@@ -0,0 +1,117 @@
// Nik Johnson
// 4-15-2024
// TA: Zachary Bi
import java.util.*;
public class ConnectFour extends AbstractStrategyGame {
private char[][] board;
private int currentPlayer;
private String[] players;
private List<Character> playerSymbols;
public ConnectFour(String player1, String player2) {
this.board = new char[6][7];
this.players = new String[2];
this.playerSymbols = new ArrayList<>();
this.currentPlayer = 0;
players[0] = player1;
players[1] = player2;
playerSymbols.add('X');
playerSymbols.add('O');
// initialize empty board
for (int i = 0; i < board.length; i++) {
for (int n = 0; n < board[i].length; n++) {
board[i][n] = ' ';
}
}
}
public String instructions() {
String output = "";
output += "Welcome to Connect Four! This game consists of a 7x6 grid, each column of";
output += " which you may choose to 'drop' a disc down by inputting numbers 1-7.\n";
output += "Player 1's symbol is 'X', Player 2's is 'O'\n";
output += "To win, you must place 4 of your discs in a row, vertically or horizontally.\n";
output += "Have fun!";
return output;
}
// constructs game board in readable format
// no parameters
// returns formatted representation of game board
public String toString() {
String output = "";
for (char[] row : board) {
for (char loc : row) {
output += "[" + loc + "]";
}
output += "\n";
}
return output;
}
public int getWinner() {
// rows
for (int i = 0; i < board.length; i++) {
for (int j = 0; j <= board[i].length - 4; j++) {
char symbol = board[i][j];
if (symbol != ' ' && board[i][j + 1] == symbol && board[i][j + 2] == symbol && board[i][j + 3] == symbol) {
return playerSymbols.indexOf(symbol) + 1;
}
}
}
// columns
for (int j = 0; j < board[0].length; j++) {
for (int i = 0; i <= board.length - 4; i++) {
char symbol = board[i][j];
if (symbol != ' ' && board[i + 1][j] == symbol && board[i + 2][j] == symbol && board[i + 3][j] == symbol) {
return playerSymbols.indexOf(symbol) + 1;
}
}
}
return -1;
}
public int getNextPlayer() {
return this.currentPlayer + 1;
}
public void makeMove(Scanner input) {
System.out.println(players[getNextPlayer() - 1] + ": Choose a column (1-7)");
int col = Integer.parseInt(input.next()) - 1;
if (col < 0 || col > 6) {
throw new IllegalArgumentException("selected column out of bounds");
}
boolean notPlaced = true;
for (int n = board.length - 1; n >= 0 && notPlaced; n--) {
if (board[n][col] == ' ') {
if (currentPlayer == 0) {
board[n][col] = 'X';
} else if (currentPlayer == 1) {
board[n][col] = 'O';
}
notPlaced = false;
}
}
if (currentPlayer == 0) {
currentPlayer = 1;
} else if (currentPlayer == 1) {
currentPlayer = 0;
}
}
}

53
c1/Testing.java Normal file
View File

@@ -0,0 +1,53 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
public class Testing {
@Test
@DisplayName("EXAMPLE TEST CASE - Small TicTacToe Example")
public void firstCaseTest() {
AbstractStrategyGame g = new TicTacToe();
// You can add optional error messages that will be displayed if a test fails
assertEquals(1, g.getNextPlayer(), "Player 1 not next player after construction");
assertEquals(-1, g.getWinner(), "Winner incorrectly declared after construction");
assertFalse(g.isGameOver(), "Game over immediately after construction");
// Weird way we're going to make moves - make our own scanners NOT
// connected to System.in. Since we can make Scanners over strings this will
// work the exact way and allow us to control input!
g.makeMove(new Scanner("0 0"));
assertEquals(2, g.getNextPlayer(), "Player 2 not next player after a single move");
assertEquals(-1, g.getWinner(), "Winner incorrectly declared after a single move");
assertFalse(g.isGameOver(), "Game over immediately after construction");
assertThrows(IllegalArgumentException.class, () -> {
// -1 is an illegal move so our code should throw an IllegalArgumentException
g.makeMove(new Scanner("-1 2"));
}, "IllegalArgumentException not thrown for illegal move");
}
@Test
@DisplayName("EXAMPLE TEST CASE - Large TicTacToe Example")
public void secondCaseTest() {
// You definitely don't have to get this fancy in your tests!
AbstractStrategyGame g = new TicTacToe();
// Going to play a whole game where 1 plays in first row, 2 plays in second row
// No optional error messages - up to you if you want your code to be easier to debug!
for (int i = 0; i < 5; i++) {
int player = (i % 2) + 1;
assertEquals(player, g.getNextPlayer());
assertFalse(g.isGameOver());
int col = i / 2;
g.makeMove(new Scanner(player + " " + col));
}
// At this point, 5 moves have been played, player 1 should have three in a row and
// player 2 should have two
assertTrue(g.isGameOver());
assertEquals(1, g.getWinner());
assertEquals(-1, g.getNextPlayer());
}
}

132
c1/TicTacToe.java Normal file
View File

@@ -0,0 +1,132 @@
// **THIS IS AN EXAMPLE IMPLEMENTATION!**
// Brett Wortzman
// CSE 123
// C0: Abstract Strategy Games
//
// A class to represent a game of tic-tac-toe that implements the
// AbstractStrategyGame interface.
import java.util.*;
public class TicTacToe extends AbstractStrategyGame {
private char[][] board;
private boolean isXTurn;
// Constructs a new TicTacToe game.
public TicTacToe() {
board = new char[][]{{'-', '-', '-'},
{'-', '-', '-'},
{'-', '-', '-'}};
isXTurn = true;
}
// Returns whether or not the game is over.
public boolean isGameOver() {
return getWinner() >= 0;
}
// Returns the index of the winner of the game.
// 1 if player 1 (X), 2 if player 2 (O), 0 if a tie occurred,
// and -1 if the game is not over.
public int getWinner() {
for (int i = 0; i < board.length; i++) {
// check row i
if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] != '-') {
return board[i][0] == 'X' ? 1 : 2;
}
// check col i
if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != '-') {
return board[0][i] == 'X' ? 1 : 2;
}
}
// check diagonals
if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] != '-') {
return board[0][0] == 'X' ? 1 : 2;
}
if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] != '-') {
return board[0][2] == 'X' ? 1 : 2;
}
// check for tie
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (board[i][j] == '-') {
// unfilled space; game not over
return -1;
}
}
}
// it's a tie!
return 0;
}
// Returns the index of which player's turn it is.
// 1 if player 1 (X), 2 if player 2 (O), -1 if the game is over
public int getNextPlayer() {
if (isGameOver()) {
return -1;
}
return isXTurn ? 1 : 2;
}
// Given the input, places an X or an O where
// the player specifies.
// Throws an IllegalArgumentException if the position is
// invalid, whether that be out of bounds or already occupied.
// Board bounds are [0, 2] for both rows and cols.
public void makeMove(Scanner input) {
char currPlayer = isXTurn ? 'X' : 'O';
System.out.print("Row? ");
int row = input.nextInt();
System.out.print("Column? ");
int col = input.nextInt();
makeMove(row, col, currPlayer);
isXTurn = !isXTurn;
}
// Private helper method for makeMove.
// Given a row and col, as well as player index,
// places an X or an O in that row and col.
// Throws an IllegalArgumentException if the position is
// invalid, whether that be out of bounds or already occupied.
// Board bounds are [0, 2] for both rows and cols.
private void makeMove(int row, int col, char player) {
if (row < 0 || row >= board.length ||
col < 0 || col >= board[0].length) {
throw new IllegalArgumentException("Invalid board position: " + row + "," + col);
}
if (board[row][col] != '-') {
throw new IllegalArgumentException("Space already occupied: " + row + "," + col);
}
board[row][col] = player;
}
// Returns a String containing instructions to play the game.
public String instructions() {
String result = "";
result += "Player 1 is X and goes first. Choose where to play by entering a row and\n";
result += "column number, where (0, 0) is the upper left and (2, 2) is the lower right.\n";
result += "Spaces show as a - are empty. The game ends when one player marks three spaces\n";
result += "in a row, in which case that player wins, or when the board is full, in which\n";
result += "case the game ends in a tie.";
return result;
}
// Returns a String representation of the current state of the board.
public String toString() {
String result = "";
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
result += board[i][j] + " ";
}
result += "\n";
}
return result;
}
}

Binary file not shown.

49
ciphers/Cipher.java Normal file
View File

@@ -0,0 +1,49 @@
import java.util.*;
import java.io.*;
// Represents a classical cipher that is able to encode a plaintext into a ciphertext, and
// decode a ciphertext into a plaintext. Also capable of encoding and decoding entire files
public abstract class Cipher {
// The minimum character able to be encoded by any cipher
public static final int MIN_CHAR = (int)(' ');
// The maximum character able to be encoded by any cipher
public static final int MAX_CHAR = (int)('}');
// The total number of characters able to be encoded by any cipher (aka. the encodable range)
public static final int TOTAL_CHARS = MAX_CHAR - MIN_CHAR + 1;
// Pre: Throws a FileNotFoundException if a file with the provided 'fileName' doesn't exist
// Post: Applies this Cipher's encryption scheme to the file with the given 'fileName', creating
// a new file to store the results.
public void encryptFile(String fileName) throws FileNotFoundException {
fileHelper(fileName, true, "-encoded");
}
// Pre: Throws a FileNotFoundException if a file with the provided 'fileName' doesn't exist
// Post: Applies this Cipher's decryption scheme (reversing a single round of encryption if applied)
// to the file with the given 'fileName', creating a new file to store the results.
public void decryptFile(String fileName) throws FileNotFoundException {
fileHelper(fileName, false, "-decoded");
}
// Pre: Throws a FileNotFoundException if a file with the provided 'fileName' doesn't exist
// Post: Reads from an input file with 'fileName', either encrypting or decrypting depending on 'encode',
// printing the results to a new file with 'suffix' appended to the input file's name
private void fileHelper(String fileName, boolean encode, String suffix) throws FileNotFoundException{
Scanner sc = new Scanner(new File(fileName));
String out = fileName.split("\\.txt")[0] + suffix + ".txt";
PrintStream ps = new PrintStream(out);
while(sc.hasNextLine()) {
String line = sc.nextLine();
ps.println(encode ? encrypt(line) : decrypt(line));
}
}
// Post: Returns the result of applying this Cipher's encryption scheme to 'input'
public abstract String encrypt(String input);
// Post: Returns the result of applying this Cipher's decryption scheme to 'input'
public abstract String decrypt(String input);
}

45
ciphers/Client.java Normal file
View File

@@ -0,0 +1,45 @@
import java.util.*;
import java.io.*;
public class Client {
// TODO: Change this line once you've implemented a cipher!
public static final Cipher CHOSEN_CIPHER = null;
// (we also encourage you to change Cipher.MIN_CHAR and Cipher.MAX_CHAR when testing!)
public static void main(String[] args) throws FileNotFoundException {
Scanner console = new Scanner(System.in);
System.out.println("Welcome to the CSE 123 cryptography application!");
System.out.println("What would you like to do?");
int chosen = -1;
do {
System.out.println();
System.out.println("(1) Encode / (2) Decode a string");
System.out.println("(3) Encode / (4) Decode a file");
System.out.println("(5) Quit");
System.out.print("Enter your choice here: ");
chosen = Integer.parseInt(console.nextLine());
while (chosen < 1 || chosen > 5) {
System.out.print("Please enter a valid option from above: ");
chosen = Integer.parseInt(console.nextLine());
}
if (chosen == 1 || chosen == 2) {
System.out.println("Please enter the string you'd like to " +
(chosen == 1 ? "encode" : "decode") + ": ");
String input = console.nextLine();
System.out.println(chosen == 1 ? CHOSEN_CIPHER.encrypt(input) :
CHOSEN_CIPHER.decrypt(input));
} else if (chosen == 3 || chosen == 4) {
System.out.print("Please enter the name of the file you'd like to " +
(chosen == 3 ? "encode" : "decode") + ": ");
String fileName = console.nextLine();
if (chosen == 3) {
CHOSEN_CIPHER.encryptFile(fileName);
} else {
CHOSEN_CIPHER.decryptFile(fileName);
}
}
} while (chosen != 5);
}
}

View File

@@ -0,0 +1,11 @@
// TODO: Write your implementation to SubstitutionRandom here!
import java.util.*;
public class SubstitutionRandom extends Substitution {
public static void main(String[] args) {
Random balls = new Random();
int boner = balls.nextInt(4);
System.out.println(boner);
Collections.shuffle()
}
}

19
ciphers/Testing.java Normal file
View File

@@ -0,0 +1,19 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.Assume.assumeTrue;
import java.util.*;
public class Testing {
@Test
@DisplayName("TODO: 1 Your Extension - ' '-'}' Shifter")
public void thirdCaseTest() {
assertTrue(false, "Not yet implemented!");
}
@Test
@DisplayName("TODO: 1 Your Extension - ' '-'}' Shifter")
public void thirdCaseTest() {
assertTrue(false, "Not yet implemented!");
}
}

View File

@@ -0,0 +1 @@
// TODO: Write your implementation to Transposition here!

1
ciphers/Vigenere.java Normal file
View File

@@ -0,0 +1 @@
// TODO: Write your implementation to Vigenere here!

1
ciphers/files/ag.txt Normal file
View File

@@ -0,0 +1 @@
HADBADCABBAGEBAG

4463
ciphers/files/hamlet.txt Normal file

File diff suppressed because it is too large Load Diff

15
ciphers/files/simple.txt Normal file
View File

@@ -0,0 +1,15 @@
Hi everyone,
We're using Ed Discussion for class Q&A.
This is the best place to ask questions about the course, whether curricular or administrative. You will get faster answers here from staff and peers than through email.
Here are some tips:
Search before you post
Heart questions and answers you find useful
Answer questions you feel confident answering
Share interesting course related content with staff and peers
For more information on Ed Discussion, you can refer to the Quick Start Guide.
All the best this semester!
Brett

Binary file not shown.

71
debugging/Book.java Normal file
View File

@@ -0,0 +1,71 @@
import java.util.*;
public class Book implements Media {
private String title;
private String author;
private List<String> authors;
private List<Integer> ratings;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public Book(String title, List<String> authors) {
this.title = title;
this.authors = authors;
}
public String getTitle() {
return this.title;
}
public List<String> getArtists() {
List<String> artists = new ArrayList<>();
if (this.author != null) {
artists.add(this.author);
}
if (this.authors != null) {
for (String author : authors) {
artists.add(author);
}
}
return artists;
}
public void addRating(int score) {
if (this.ratings == null) {
ratings = new ArrayList<>();
}
this.ratings.add(score);
}
public int getNumRatings() {
if (this.ratings == null) {
return 0;
}
return this.ratings.size();
}
public double getAverageRating() {
if (this.ratings == null) {
return 0;
}
int sum = 0;
for (int rating : ratings) {
sum += rating;
}
return (double)sum / this.ratings.size();
}
public String toString() {
return this.title + " by " + this.getArtists() + ": " + this.getAverageRating() +
(this.ratings.size()) + " ratings";
}
}

54
debugging/Debugging.java Normal file
View File

@@ -0,0 +1,54 @@
import java.util.*;
public class Debugging {
public static void main(String[] args) {
Map<String, Set<Integer>> testMap = new TreeMap<>();
Set<Integer> c121 = arrToSet(new int[]{42, 17, 42, 42});
Set<Integer> c122 = arrToSet(new int[]{10, 12, 14});
Set<Integer> c123 = arrToSet(new int[]{100, 99, 98, -97});
testMap.put("cse121", c121);
testMap.put("cse122", c122);
testMap.put("cse123", c123);
Map<String, Set<Integer>> deepCopyMap = deepCopy(testMap);
if (deepCopyMap.isEmpty()) {
System.out.println("{}");
} else {
String line = "";
for (String key : deepCopyMap.keySet()) {
line += key + "=" + deepCopyMap.get(key).toString() + ", ";
}
System.out.println("{" + line.substring(0, line.length() - 2) + "}");
}
}
public static Set<Integer> arrToSet(int[] arr) {
Set<Integer> s = new TreeSet<>();
for (int num : arr) {
s.add(num);
}
return s;
}
// Produces and returns a "deep copy" of the parameter map, which has the same
// structure and values as the parameter, but with all internal data structures
// and values copied. After calling this method, modifying the parameter or
// return value should NOT affect the other.
//
// Parameters:
// inputMap - the map to duplicate
//
// Returns:
// A deep copy of the parameter map.
public static Map<String, Set<Integer>> deepCopy(Map<String, Set<Integer>> inputMap) {
Map<String, Set<Integer>> deepCopy = new TreeMap<>();
for (String key : inputMap.keySet()) {
Set<Integer> inputSet = new TreeSet<>(inputMap.get(key));
// Set<Integer> inputSet = inputMap.get(key);
deepCopy.put(key, inputSet);
}
return deepCopy;
}
}

View File

@@ -0,0 +1,52 @@
import java.util.*;
public class InvertedIndex {
public static void main(String[] args) {
List<String> docs = new ArrayList<>();
docs.add("Raiders of the Lost Ark");
docs.add("The Temple of Doom");
docs.add("The Last Crusade");
Map<String, Set<String>> result = createIndex(docs);
System.out.println(docs);
System.out.println();
System.out.println(result);
}
// TODO: Write and document your createIndex method here
public static Map<String, Set<String>> createIndex(List<String> docs) {
Map<String, Set<String>> index = new TreeMap<>();
Set<String> uniqueWords = getUniqueWords(docs);
for (String uniqueWord : uniqueWords) {
index.put(uniqueWord.toLowerCase(), new HashSet<String>());
}
for (String word : index.keySet()) {
for (int i = 0; i < docs.size(); i++) {
Scanner wordScanner = new Scanner(docs.get(i));
while (wordScanner.hasNext()) {
if (wordScanner.next().equalsIgnoreCase(word)) {
index.get(word).add(docs.get(i));
}
}
}
}
return index;
}
public static Set<String> getUniqueWords(List<String> docs) {
Set<String> uniqueWords = new HashSet<>();
for (String title : docs) {
Scanner titleScanner = new Scanner(title);
while (titleScanner.hasNext()) {
uniqueWords.add(titleScanner.next());
}
titleScanner.close();
}
return uniqueWords;
}
}

41
debugging/Media.java Normal file
View File

@@ -0,0 +1,41 @@
import java.util.*;
/**
* An interface to represent various types of media (movies, books, tv shows, songs, etc.).
*/
public interface Media {
/**
* Gets the title of this media.
*
* @return The title of this media.
*/
public String getTitle();
/**
* Gets all artists associated with this media.
*
* @return A list of artists for this media.
*/
public List<String> getArtists();
/**
* Adds a rating to this media.
*
* @param score The score for the new rating. Should be non-negative.
*/
public void addRating(int score);
/**
* Gets the number of times this media has been rated.
*
* @return The number of ratings for this media.
*/
public int getNumRatings();
/**
* Gets the average (mean) of all ratings for this media.
*
* @return The average (mean) of all ratings for this media. If no ratings exist, returns 0.
*/
public double getAverageRating();
}

40
debugging/Testing.java Normal file
View File

@@ -0,0 +1,40 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
public class Testing {
@Test
@DisplayName("EXAMPLE TEST CASE - createIndex Example")
public void firstCaseTest() {
List<String> documents = new ArrayList<>(List.of("The Bee Movie is great!",
"I love the Bee Movie",
"Y'all seen Dune 2?"));
Map<String, Set<String>> index = InvertedIndex.createIndex(documents);
// Make sure that tokens are correctly converted to lower case
assertTrue(index.containsKey("bee"));
assertFalse(index.containsKey("Bee"));
// Make sure that punctuation is ignored
assertTrue(index.containsKey("great!"));
assertFalse(index.containsKey("great"));
// Check one of the sets
assertEquals(Set.of("The Bee Movie is great!",
"I love the Bee Movie"),
index.get("movie"));
}
@Test
@DisplayName("EXAMPLE TEST CASE - Book 2 String constructor + getters")
public void secondCaseTest() {
Book b = new Book("Title", "Author");
// Test all getters after constructing with 2 Strings
assertEquals("Title", b.getTitle());
assertEquals(List.of("Author"), b.getArtists());
assertEquals(0, b.getNumRatings());
assertEquals(0.0, b.getAverageRating());
}
}

21
p1/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,21 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Current File",
"request": "launch",
"mainClass": "${file}"
},
{
"type": "java",
"name": "Client",
"request": "launch",
"mainClass": "Client",
"projectName": "p1_b4eddd44"
}
]
}

91
p1/Client.java Normal file
View File

@@ -0,0 +1,91 @@
import java.util.*;
// A program to work with Mini-Git. Manages the state of repositories and allows for all
// operations defined in Mini-Git.
public class Client {
private static List<String> ops = new ArrayList<>();
public static void main(String[] args) {
Collections.addAll(ops, "create", "head", "history", "commit", "drop",
"synchronize", "quit");
Scanner console = new Scanner(System.in);
Map<String, Repository> repos = new HashMap<>();
String op = "";
String name = "";
intro();
while (!op.equalsIgnoreCase("quit")) {
System.out.println("Available repositories: ");
for (Repository repo : repos.values()) {
System.out.println("\t" + repo);
}
System.out.println("Operations: " + ops);
System.out.print("Enter operation and repository: ");
String[] input = console.nextLine().split("\\s+");
op = input[0];
name = input.length > 1 ? input[1] : "";
while (!ops.contains(op) || (!op.equalsIgnoreCase("create") &&
!op.equalsIgnoreCase("quit") &&
!repos.containsKey(name))) {
System.out.println(" **ERROR**: Operation or repository not recognized.");
System.out.print("Enter operation and repository: ");
input = console.nextLine().split("\\s+");
op = input[0];
name = input.length > 1 ? input[1] : "";
}
Repository currRepo = repos.get(name);
op = op.toLowerCase();
if (op.equalsIgnoreCase("create")) {
if (currRepo != null) {
System.out.println(" **ERROR**: Repository with that name already exists.");
} else {
Repository newRepo = new Repository(name);
repos.put(name, newRepo);
System.out.println(" New repository created: " + newRepo);
}
} else if (op.equalsIgnoreCase("head")) {
System.out.println(currRepo.getRepoHead());
} else if (op.equalsIgnoreCase("history")) {
System.out.print("How many commits back? ");
int nHist = console.nextInt();
console.nextLine();
System.out.println(currRepo.getHistory(nHist));
} else if (op.equalsIgnoreCase("commit")) {
System.out.print("Enter commit message: ");
String message = console.nextLine();
System.out.println(" New commit: " + currRepo.commit(message));
} else if (op.equalsIgnoreCase("drop")) {
System.out.print("Enter ID to drop: ");
String idDrop = console.nextLine();
if (currRepo.drop(idDrop)) {
System.out.println(" Successfully dropped " + idDrop);
} else {
System.out.println(" No commit dropped!");
}
} else if (op.equalsIgnoreCase("synchronize")) {
System.out.print("Which repository would you like to " +
"synchronize into the given one? ");
String repo = console.nextLine();
if (repo.equals(name)) {
System.out.println("Cannot synchronize the same repositories!");
} else if (!repos.containsKey(repo)) {
System.out.println("Repository does not exist!");
} else {
currRepo.synchronize(repos.get(repo));
}
}
System.out.println();
}
}
// Prints out an introduction to the Mini-Git test client.
public static void intro() {
System.out.println("Welcome to the Mini-Git test client!");
System.out.println("Use this program to test your Mini-Git repository implemenation.");
System.out.println("Make sure to test all operations in all cases --");
System.out.println("some cases are particularly tricky.");
System.out.println();
}
}

148
p1/ExampleTesting.java Normal file
View File

@@ -0,0 +1,148 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
public class Testing {
private Repository repo1;
private Repository repo2;
// Occurs before each of the individual test cases
// (creates new repos and resets commit ids)
@BeforeEach
public void setUp() {
repo1 = new Repository("repo1");
repo2 = new Repository("repo2");
Repository.Commit.resetIds();
}
@Test
@DisplayName("EXAMPLE TEST - getHistory()")
public void getHistory() throws InterruptedException {
// Initialize commit messages
String[] commitMessages = new String[]{"Initial commit.",
"Updated method documentation.",
"Removed unnecessary object creation."};
commitAll(repo1, commitMessages);
testHistory(repo1, 1, commitMessages);
}
@Test
@DisplayName("EXAMPLE TEST - drop() (empty case)")
public void testDropEmpty() {
assertFalse(repo1.drop("123"));
}
@Test
@DisplayName("EXAMPLE TEST - drop() (front case)")
public void testDropFront() throws InterruptedException {
// Initialize commit messages
commitAll(repo1, new String[]{"First commit"}); // ID "0"
commitAll(repo2, new String[]{"Added unit tests."}); // ID "1"
// Assert that repo1 successfully dropped "0"
assertTrue(repo1.drop("0"));
assertEquals(repo1.getRepoSize(), 0);
// Assert that repo2 does not drop "0" but drops "1"
// (Note that the commit ID increments regardless of the repository!)
assertFalse(repo2.drop("0"));
assertTrue(repo2.drop("1"));
assertEquals(repo2.getRepoSize(), 0);
}
@Test
@DisplayName("EXAMPLE TEST - synchronize() (one: [1, 2], two: [3, 4])")
public void testSynchronizeOne() throws InterruptedException {
// Initialize commit messages
commitAll(repo1, new String[]{"One", "Two"});
commitAll(repo2, new String[]{"Three", "Four"});
// Make sure both repos got exactly 2 commits each
assertEquals(2, repo1.getRepoSize());
assertEquals(2, repo2.getRepoSize());
// Synchronize repo2 into repo1
repo1.synchronize(repo2);
assertEquals(4, repo1.getRepoSize());
assertEquals(0, repo2.getRepoSize());
// Make sure the history of repo1 is correctly synchronized
testHistory(repo1, 4, new String[]{"One", "Two", "Three", "Four"});
}
// Commits all of the provided messages into the provided repo, making sure timestamps
// are correctly sequential (no ties). If used, make sure to include
// 'throws InterruptedException'
// much like we do with 'throws FileNotFoundException'. Example useage:
//
// repo1:
// head -> null
// To commit the messages "one", "two", "three", "four"
// commitAll(repo1, new String[]{"one", "two", "three", "four"})
// This results in the following after picture
// repo1:
// head -> "four" -> "three" -> "two" -> "one" -> null
//
// YOU DO NOT NEED TO UNDERSTAND HOW THIS METHOD WORKS TO USE IT!
// (this is why documentation is important!)
public void commitAll(Repository repo, String[] messages) throws InterruptedException {
// Commit all of the provided messages
for (String message : messages) {
int size = repo.getRepoSize();
repo.commit(message);
// Make sure exactly one commit was added to the repo
assertEquals(size + 1, repo.getRepoSize(),
String.format("Size not correctly updated after commiting message [%s]",
message));
// Sleep to guarantee that all commits have different time stamps
Thread.sleep(2);
}
}
// Makes sure the given repositories history is correct up to 'n' commits, checking against
// all commits made in order. Example useage:
//
// repo1:
// head -> "four" -> "three" -> "two" -> "one" -> null
// (Commits made in the order ["one", "two", "three", "four"])
// To test the getHistory() method up to n=3 commits this can be done with:
// testHistory(repo1, 3, new String[]{"one", "two", "three", "four"})
// Similarly, to test getHistory() up to n=4 commits you'd use:
// testHistory(repo1, 4, new String[]{"one", "two", "three", "four"})
//
// YOU DO NOT NEED TO UNDERSTAND HOW THIS METHOD WORKS TO USE IT!
// (this is why documentation is important!)
public void testHistory(Repository repo, int n, String[] allCommits) {
int totalCommits = repo.getRepoSize();
assertTrue(n <= totalCommits,
String.format("Provided n [%d] too big. Only [%d] commits",
n, totalCommits));
String[] nCommits = repo.getHistory(n).split("\n");
assertTrue(nCommits.length <= n,
String.format("getHistory(n) returned more than n [%d] commits", n));
assertTrue(nCommits.length <= allCommits.length,
String.format("Not enough expected commits to check against. " +
"Expected at least [%d]. Actual [%d]",
n, allCommits.length));
for (int i = 0; i < n; i++) {
String commit = nCommits[i];
// Old commit messages/ids are on the left and the more recent commit messages/ids are
// on the right so need to traverse from right to left
int backwardsIndex = totalCommits - 1 - i;
String commitMessage = allCommits[backwardsIndex];
assertTrue(commit.contains(commitMessage),
String.format("Commit [%s] doesn't contain expected message [%s]",
commit, commitMessage));
assertTrue(commit.contains("" + backwardsIndex),
String.format("Commit [%s] doesn't contain expected id [%d]",
commit, backwardsIndex));
}
}
}

146
p1/Repository.java Normal file
View File

@@ -0,0 +1,146 @@
import java.util.*;
import java.text.SimpleDateFormat;
public class Repository {
private String name;
private LinkedList<Commit> commits;
public Repository(String name) {
if (name.isEmpty() || name == null) {
throw new IllegalArgumentException("Repository must have a name!");
}
this.name = name;
this.commits = new LinkedList<>();
}
public String getRepoHead() {
return commits.peek().id;
}
public int getRepoSize() {
return commits.size();
}
public String toString() {
return name + " - Current head: " + commits.peek().toString();
}
public boolean contains(String targetId) {
for (Commit commit : commits) {
if (commit.id == targetId) {
return true;
}
}
return false;
}
public String getHistory(int n) {
String output = "";
for (int i = 0; i < commits.size() - 1; i++) {
output += commits.get(i).toString() + "\n";
}
return output;
}
public String commit(String message) {
commits.add(new Commit(message));
return commits.peek().id;
}
public boolean drop(String targetId) {
for (int n = 0; n < commits.size() - 1; n ++) {
if (commits.get(n).id == targetId) {
commits.remove(n);
return true;
}
}
return false;
}
public void synchronize(Repository other) {
}
/**
* DO NOT MODIFY
* A class that represents a single commit in the repository.
* Commits are characterized by an identifier, a commit message,
* and the time that the commit was made. A commit also stores
* a reference to the immediately previous commit if it exists.
*
* Staff Note: You may notice that the comments in this
* class openly mention the fields of the class. This is fine
* because the fields of the Commit class are public. In general,
* be careful about revealing implementation details!
*/
public class Commit {
private static int currentCommitID;
/**
* The time, in milliseconds, at which this commit was created.
*/
public final long timeStamp;
/**
* A unique identifier for this commit.
*/
public final String id;
/**
* A message describing the changes made in this commit.
*/
public final String message;
/**
* A reference to the previous commit, if it exists. Otherwise, null.
*/
public Commit past;
/**
* Constructs a commit object. The unique identifier and timestamp
* are automatically generated.
* @param message A message describing the changes made in this commit.
* @param past A reference to the commit made immediately before this
* commit.
*/
public Commit(String message, Commit past) {
this.id = "" + currentCommitID++;
this.message = message;
this.timeStamp = System.currentTimeMillis();
this.past = past;
}
/**
* Constructs a commit object with no previous commit. The unique
* identifier and timestamp are automatically generated.
* @param message A message describing the changes made in this commit.
*/
public Commit(String message) {
this(message, null);
}
/**
* Returns a string representation of this commit. The string
* representation consists of this commit's unique identifier,
* timestamp, and message, in the following form:
* "[identifier] at [timestamp]: [message]"
* @return The string representation of this collection.
*/
@Override
public String toString() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
Date date = new Date(timeStamp);
return id + " at " + formatter.format(date) + ": " + message;
}
/**
* Resets the IDs of the commit nodes such that they reset to 0.
* Primarily for testing purposes.
*/
public static void resetIds() {
Commit.currentCommitID = 0;
}
}
}

99
p1/Testing.java Normal file
View File

@@ -0,0 +1,99 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
public class Testing {
private Repository repo1;
private Repository repo2;
// Occurs before each of the individual test cases
// (creates new repos and resets commit ids)
@BeforeEach
public void setUp() {
repo1 = new Repository("repo1");
repo2 = new Repository("repo2");
Repository.Commit.resetIds();
}
// TODO: Write your tests here!
/////////////////////////////////////////////////////////////////////////////////
// PROVIDED HELPER METHODS (You don't have to use these if you don't want to!) //
/////////////////////////////////////////////////////////////////////////////////
// Commits all of the provided messages into the provided repo, making sure timestamps
// are correctly sequential (no ties). If used, make sure to include
// 'throws InterruptedException'
// much like we do with 'throws FileNotFoundException'. Example useage:
//
// repo1:
// head -> null
// To commit the messages "one", "two", "three", "four"
// commitAll(repo1, new String[]{"one", "two", "three", "four"})
// This results in the following after picture
// repo1:
// head -> "four" -> "three" -> "two" -> "one" -> null
//
// YOU DO NOT NEED TO UNDERSTAND HOW THIS METHOD WORKS TO USE IT! (this is why documentation
// is important!)
public void commitAll(Repository repo, String[] messages) throws InterruptedException {
// Commit all of the provided messages
for (String message : messages) {
int size = repo.getRepoSize();
repo.commit(message);
// Make sure exactly one commit was added to the repo
assertEquals(size + 1, repo.getRepoSize(),
String.format("Size not correctly updated after commiting message [%s]",
message));
// Sleep to guarantee that all commits have different time stamps
Thread.sleep(2);
}
}
// Makes sure the given repositories history is correct up to 'n' commits, checking against
// all commits made in order. Example useage:
//
// repo1:
// head -> "four" -> "three" -> "two" -> "one" -> null
// (Commits made in the order ["one", "two", "three", "four"])
// To test the getHistory() method up to n=3 commits this can be done with:
// testHistory(repo1, 3, new String[]{"one", "two", "three", "four"})
// Similarly, to test getHistory() up to n=4 commits you'd use:
// testHistory(repo1, 4, new String[]{"one", "two", "three", "four"})
//
// YOU DO NOT NEED TO UNDERSTAND HOW THIS METHOD WORKS TO USE IT! (this is why documentation
// is important!)
public void testHistory(Repository repo, int n, String[] allCommits) {
int totalCommits = repo.getRepoSize();
assertTrue(n <= totalCommits,
String.format("Provided n [%d] too big. Only [%d] commits",
n, totalCommits));
String[] nCommits = repo.getHistory(n).split("\n");
assertTrue(nCommits.length <= n,
String.format("getHistory(n) returned more than n [%d] commits", n));
assertTrue(nCommits.length <= allCommits.length,
String.format("Not enough expected commits to check against. " +
"Expected at least [%d]. Actual [%d]",
n, allCommits.length));
for (int i = 0; i < n; i++) {
String commit = nCommits[i];
// Old commit messages/ids are on the left and the more recent commit messages/ids are
// on the right so need to traverse from right to left
int backwardsIndex = totalCommits - 1 - i;
String commitMessage = allCommits[backwardsIndex];
assertTrue(commit.contains(commitMessage),
String.format("Commit [%s] doesn't contain expected message [%s]",
commit, commitMessage));
assertTrue(commit.contains("" + backwardsIndex),
String.format("Commit [%s] doesn't contain expected id [%d]",
commit, backwardsIndex));
}
}
}

88
search-engine/Book.java Normal file
View File

@@ -0,0 +1,88 @@
// Nik Johnson
// TA: Zachary Bi
// 4-2-2024
import java.util.*;
import java.text.DecimalFormat;
// book object, which can have a title, authors, and a list of ratings
public class Book implements Media {
private String title;
private String author;
private List<String> authors;
private List<Integer> ratings;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public Book(String title, List<String> authors) {
this.title = title;
this.authors = authors;
}
public String getTitle() {
return this.title;
}
public List<String> getArtists() {
List<String> artists = new ArrayList<>();
if (this.author != null) {
artists.add(this.author);
}
if (this.authors != null) {
for (String author : authors) {
artists.add(author);
}
}
return artists;
}
public void addRating(int score) {
if (this.ratings == null) {
ratings = new ArrayList<>();
}
this.ratings.add(score);
}
public int getNumRatings() {
if (this.ratings == null) {
return 0;
}
return this.ratings.size();
}
public double getAverageRating() {
if (this.ratings == null) {
return 0;
}
int sum = 0;
for (int rating : ratings) {
sum += rating;
}
return (double)sum / this.ratings.size();
}
public String toString() {
String output = "";
output += this.title + " by " + this.getArtists() + ": ";
DecimalFormat df = new DecimalFormat("#." + "0".repeat(2));
if (ratings != null) {
output += df.format(this.getAverageRating()) + " (" + (this.ratings.size()) + " ratings" + ")";
return output;
}
return output + "No ratings yet!";
}
}

41
search-engine/Media.java Normal file
View File

@@ -0,0 +1,41 @@
import java.util.*;
/**
* An interface to represent various types of media (movies, books, tv shows, songs, etc.).
*/
public interface Media {
/**
* Gets the title of this media.
*
* @return The title of this media.
*/
public String getTitle();
/**
* Gets all artists associated with this media.
*
* @return A list of artists for this media.
*/
public List<String> getArtists();
/**
* Adds a rating to this media.
*
* @param score The score for the new rating. Should be non-negative.
*/
public void addRating(int score);
/**
* Gets the number of times this media has been rated.
*
* @return The number of ratings for this media.
*/
public int getNumRatings();
/**
* Gets the average (mean) of all ratings for this media.
*
* @return The average (mean) of all ratings for this media. If no ratings exist, returns 0.
*/
public double getAverageRating();
}

View File

@@ -0,0 +1,213 @@
import java.io.*;
import java.util.*;
// Name: Niklas Johnson
// Date: 4-2-2024
// This class allows users to find and rate books within BOOK_DIRECTORY
// containing certain terms. Also handles omitting items if the query is prefixed
// with a '-'
public class SearchClient {
public static final String BOOK_DIRECTORY = "./books";
public static void main(String[] args) throws FileNotFoundException {
Scanner console = new Scanner(System.in);
List<Book> books = loadBooks();
List<Media> media = new ArrayList<>(books);
Map<String, Set<Media>> index = createIndex(media);
System.out.println("Welcome to the CSE 123 Search Engine!");
String command = "";
while (!command.equalsIgnoreCase("quit")) {
System.out.println("What would you like to do? [Search, Rate, Quit]");
System.out.print("> ");
command = console.nextLine();
if (command.equalsIgnoreCase("search")) {
searchQuery(console, index);
} else if (command.equalsIgnoreCase("rate")) {
addRating(console, media);
} else if (!command.equalsIgnoreCase("quit")) {
System.out.println("Invalid command, please try again.");
}
}
System.out.println("See you next time!");
}
// Produces and returns a "deep copy" of the parameter map, which has the same
// structure and values as the parameter, but with all internal data structures
// and values copied. After calling this method, modifying the parameter or
// return value should NOT affect the other.
//
// Parameters:
// inputMap - the map to duplicate
//
// Returns:
// A deep copy of the parameter map.
// TODO: Copy fixed version and update to use Media
public static Map<String, Set<Media>> deepCopy(Map<String, Set<Media>> inputMap) {
Map<String, Set<Media>> deepCopy = new TreeMap<>();
for (String key : inputMap.keySet()) {
Set<Media> inputSet = new HashSet<>(inputMap.get(key));
deepCopy.put(key, inputSet);
}
return deepCopy;
}
// creates a directory of words that appear in a given list of Strings
// takes a list of strings to process
// returns sorted map containing the index
// no thrown exceptions
public static Map<String, Set<Media>> createIndex(List<Media> media) throws FileNotFoundException {
Map<String, Set<Media>> index = new TreeMap<>();
Set<String> uniqueWords = getUniqueWords(media);
for (String uniqueWord : uniqueWords) {
index.put(uniqueWord.toLowerCase(), new HashSet<Media>());
}
for (String word : index.keySet()) {
for (int i = 0; i < media.size(); i++) {
Scanner titleScanner = new Scanner(media.get(i).getTitle());
while (titleScanner.hasNext()) {
if (titleScanner.next().equalsIgnoreCase(word)) {
index.get(word).add(media.get(i));
}
}
titleScanner.close();
for (String artists : media.get(i).getArtists()) {
Scanner artistScanner = new Scanner(artists);
while (artistScanner.hasNext()) {
if (artistScanner.next().equalsIgnoreCase(word)) {
index.get(word).add(media.get(i));
}
}
artistScanner.close();
}
}
}
return index;
}
// helper method for createIndex. looks through given list of strings,
// keeping track of only unique words
// takes list of words to prune
// returns pruned set of words
// no thrown exceptions
public static Set<String> getUniqueWords(List<Media> media) throws FileNotFoundException {
Set<String> uniqueWords = new HashSet<>();
for (Media thing : media) {
Scanner mediaScanner = new Scanner(new File(BOOK_DIRECTORY + "/" + thing.getTitle() + ".txt"));
while (mediaScanner.hasNext()) {
uniqueWords.add(mediaScanner.next());
}
mediaScanner.close();
}
return uniqueWords;
}
// Allows the user to search a specific query using the provided 'index' to find appropraite
// Media entries.
//
// Parameters:
// console - the Scanner to get user input from
// index - invertedIndex mapping terms to the Set of media containing those terms
public static void searchQuery(Scanner console, Map<String, Set<Media>> index) {
System.out.println("Enter query:");
System.out.print("> ");
String query = console.nextLine();
Optional<Set<Media>> result = search(deepCopy(index), query);
if (result.isEmpty()) {
System.out.println("\tNo results!");
} else {
for (Media m : result.get()) {
System.out.println("\t" + m.toString());
}
}
}
// Allows the user to add a rating to one of the options wthin 'media'
//
// Parameters:
// console - the Scanner to get user input from
// media - list of all media options loaded into the search engine
public static void addRating(Scanner console, List<Media> media) {
System.out.print("[" + media.get(0).getTitle());
for (int i = 1; i < media.size(); i++) {
System.out.print(", " + media.get(i).getTitle());
}
System.out.println("]");
System.out.println("What would you like to rate (enter index)?");
System.out.print("> ");
int choice = Integer.parseInt(console.nextLine());
if (choice < 0 || choice >= media.size()) {
System.out.println("Invalid choice");
} else {
System.out.println("Rating [" + media.get(choice).getTitle() + "]");
System.out.println("What rating would you give?");
System.out.print("> ");
int rating = Integer.parseInt(console.nextLine());
media.get(choice).addRating(rating);
}
}
// Searches a specific query using the provided 'index' to find appropraite Media entries.
// terms are determined by whitespace separation. If a term is proceeded by '-' any entry
// containing that term will be removed from the result.
//
// Parameters:
// index - invertedIndex mapping terms to the Set of media containing those terms
// query - user's entered query string to use in searching
//
// Returns:
// An optional set of all Media containing the requirested terms. If none, Optional.Empty()
public static Optional<Set<Media>> search(Map<String, Set<Media>> index, String query) {
Optional<Set<Media>> ret = Optional.empty();
Scanner tokens = new Scanner(query);
while (tokens.hasNext()) {
boolean minus = false;
String token = tokens.next().toLowerCase();
if (token.startsWith("-")) {
minus = true;
token = token.substring(1);
}
if (index.containsKey(token)) {
if (ret.isEmpty() && !minus) {
ret = Optional.of(index.get(token));
} else if (!ret.isEmpty() && minus) {
ret.get().removeAll(index.get(token));
} else if (!ret.isEmpty() && !minus) {
ret.get().retainAll(index.get(token));
}
}
}
return ret;
}
// Loads all books from BOOK_DIRECTORY. Assumes that each book starts with two lines -
// "Title: " which is followed by the book's title
// "Author: " which is followed by the book's author
//
// Returns:
// A list of all book objects corresponding to the ones located in BOOK_DIRECTORY
public static List<Book> loadBooks() throws FileNotFoundException {
List<Book> ret = new ArrayList<>();
File dir = new File(BOOK_DIRECTORY);
for (File f : dir.listFiles()) {
Scanner sc = new Scanner(f);
String title = sc.nextLine().substring("Title: ".length());
String author = sc.nextLine().substring("Author: ".length());
ret.add(new Book(title, author));
}
return ret;
}
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff