Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,4 @@ triage-*.md

# jqwik failure database (replays previously failing samples locally)
.jqwik-database
findings.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,30 @@ public String getName() {
}

/**
* Compares this element with another by name. The argument is cast without a type check, so passing an object
* that is not an {@code Element} fails with a {@code ClassCastException}. {@code hashCode} is not overridden.
* Compares this element with another by name.
*
* @param obj the object to compare with; must be an {@code Element} or {@code null}
* @return {@code true} if {@code obj} is an element with an equal name, {@code false} if {@code obj} is
* {@code null}
* @param obj the object to compare with
* @return {@code true} if {@code obj} is an element with an equal name, {@code false} otherwise (including
* {@code null} and objects of other types)
*/
@Override
public boolean equals(final Object obj) {
if (obj == null) {
if (!(obj instanceof final Element other)) {
return false;
}
final Element other = (Element) obj;
return other.getName().equals(this.getName());
}

/**
* Returns a hash code consistent with {@link #equals(Object)}: elements with equal names hash alike.
*
* @return the hash code of the name
*/
@Override
public int hashCode() {
return this.getName().hashCode();
}

/**
* Returns the element's name.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ public final class ArrayUtils {

private ArrayUtils() {}

private static final Random rand = new Random();
private static final Random RANDOM = new Random();

/**
* Function for doing an in-place ↴ shuffle of an array.
* Function for doing an in-place (Fisher–Yates) shuffle of an array.
* <p>
* Solution: We choose a random item to move to the first index, then we choose a random other item to move to the second index, etc.
* We "place" an item in an index by swapping it with the item currently at that index.
Expand All @@ -22,33 +22,33 @@ private ArrayUtils() {}
* <p>
* Arrays with zero or one element are left unchanged.
*
* @param theArray the array to shuffle; modified in place
* @param values the array to shuffle; modified in place
*/
public static void shuffle(final int[] theArray) {
public static void shuffle(final int[] values) {
// if it's 1 or 0 items, just return
if (theArray.length <= 1) {
if (values.length <= 1) {
return;
}

// walk through from beginning to end
for (int indexWeAreChoosingFor = 0; indexWeAreChoosingFor < theArray.length - 1; indexWeAreChoosingFor++) {
for (int targetIndex = 0; targetIndex < values.length - 1; targetIndex++) {

// choose a random not-yet-placed item to place there
// (could also be the item currently in that spot)
// must be an item AFTER the current item, because the stuff
// before has all already been placed
final int randomChoiceIndex = getRandom(indexWeAreChoosingFor, theArray.length - 1);
final int randomChoiceIndex = randomBetweenInclusive(targetIndex, values.length - 1);

// place our random choice in the spot by swapping
if (randomChoiceIndex != indexWeAreChoosingFor) {
final int valueAtIndexWeChoseFor = theArray[indexWeAreChoosingFor];
theArray[indexWeAreChoosingFor] = theArray[randomChoiceIndex];
theArray[randomChoiceIndex] = valueAtIndexWeChoseFor;
if (randomChoiceIndex != targetIndex) {
final int displacedValue = values[targetIndex];
values[targetIndex] = values[randomChoiceIndex];
values[randomChoiceIndex] = displacedValue;
}
}
}

private static int getRandom(final int floor, final int ceiling) {
return rand.nextInt((ceiling - floor) + 1) + floor;
private static int randomBetweenInclusive(final int min, final int max) {
return RANDOM.nextInt((max - min) + 1) + min;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,16 @@ public void addAtEnd(final Element element) {
/**
* Inserts an element at the given position, shifting later elements one position back.
* <p>
* If the list is empty the element is added regardless of {@code position}. If {@code position} equals
* {@link #size()} the element is appended. Otherwise the element is linked after the node at
* {@code position - 1}, so inserting at position {@code 0} of a non-empty list is not supported.
* If the list is empty the element is added regardless of {@code position}. Position {@code 0} inserts at the
* start and {@link #size()} appends; otherwise the element is linked after the node at {@code position - 1}.
*
* @param position the position for the new element, from {@code 1} to {@link #size()} on a non-empty list
* @param position the position for the new element, from {@code 0} to {@link #size()}
* @param element the element to insert
* @throws IllegalArgumentException if the list is not empty and {@code position - 1} is not an occupied position
* (including {@code position == 0})
* @throws IllegalArgumentException if the list is not empty and {@code position} is negative or greater than
* {@link #size()}
*/
public void add(final int position, final Element element) {
if (this.totalElements == 0) {
if (this.totalElements == 0 || position == 0) {
addAtStart(element);
} else if (position == this.totalElements) {
addAtEnd(element);
Expand All @@ -88,7 +87,7 @@ private boolean positionOccupied(final int position) {

private LinkedListNode getNode(final int position) {
if (!positionOccupied(position)) {
throw new IllegalArgumentException("Posição inexistente");
throw new IllegalArgumentException("Invalid position: " + position);
}
LinkedListNode current = this.first;
for (int i = 0; i < position; i++) {
Expand Down Expand Up @@ -124,22 +123,28 @@ public Element get(final int position) {
*/
public void removeFromStart() {
if (this.totalElements == 0) {
throw new IllegalStateException("Lista vazia");
throw new IllegalStateException("List is empty");
}

this.first = this.first.getNext();
this.totalElements--;

if (this.totalElements == 0) {
this.last = null;
} else {
this.first.setPrevious(null);
}
}

/**
* Removes the last element. Unlike {@link #removeFromStart()}, calling this on an empty list fails with a
* {@code NullPointerException} rather than an {@code IllegalStateException}.
* Removes the last element.
*
* @throws IllegalStateException if the list is empty
*/
public void removeFromEnd() {
if (this.totalElements == 0) {
throw new IllegalStateException("List is empty");
}
if (this.totalElements == 1) {
this.removeFromStart();
} else {
Expand All @@ -155,21 +160,20 @@ public void removeFromEnd() {
* <p>
* Position {@code 0} delegates to {@link #removeFromStart()} and position {@code size() - 1} to
* {@link #removeFromEnd()}; any other position unlinks the node after {@code position - 1}.
* <p>
* Positions are not fully validated: on a non-empty list, {@code position == size()} fails with a
* {@code NullPointerException}, as does {@code -1} on an empty list.
*
* @param position the zero-based position, from {@code 0} to {@code size() - 1}
* @throws IllegalStateException if {@code position} is {@code 0} and the list is empty
* @throws IllegalArgumentException if {@code position} is negative or greater than {@link #size()}, except
* {@code -1} on an empty list
* @throws IllegalStateException if the list is empty and {@code position} is {@code 0} or {@code -1}
* @throws IllegalArgumentException if {@code position} is otherwise not an occupied position
*/
public void remove(final int position) {
if (position == 0) {
this.removeFromStart();
} else if (position == totalElements - 1) {
this.removeFromEnd();
} else {
if (!positionOccupied(position)) {
throw new IllegalArgumentException("Invalid position: " + position);
}
final LinkedListNode previous = this.getNode(position - 1);
final LinkedListNode current = previous.getNext();
final LinkedListNode next = current.getNext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@
*/
public class Set {

private static final int BUCKET_COUNT = 26;

private final List<LinkedList<String>> table = new LinkedList<LinkedList<String>>();

/** Creates an empty set with 26 empty buckets. */
public Set() {
for (int i = 0; i < 26; i++) {
for (int i = 0; i < BUCKET_COUNT; i++) {
this.table.add(new LinkedList<String>());
}
}

/**
* Adds a word if it is not already present; duplicates are ignored. Comparison is case-sensitive. An empty
* string fails with a {@code StringIndexOutOfBoundsException}.
* Adds a word if it is not already present; duplicates are ignored. Comparison is case-sensitive.
*
* @param word the word to add
* @throws IllegalArgumentException if {@code word} is empty
*/
public void add(final String word) {
if (!contains(word)) {
Expand All @@ -34,10 +36,10 @@ public void add(final String word) {
}

/**
* Removes a word if present; otherwise does nothing. An empty string fails with a
* {@code StringIndexOutOfBoundsException}.
* Removes a word if present; otherwise does nothing.
*
* @param word the word to remove
* @throws IllegalArgumentException if {@code word} is empty
*/
public void remove(final String word) {
if (contains(word)) {
Expand All @@ -53,7 +55,10 @@ private boolean contains(final String word) {
}

private int calculateTableIndex(final String word) {
return word.toLowerCase().charAt(0) % 26;
if (word.isEmpty()) {
throw new IllegalArgumentException("word must not be empty");
}
return word.toLowerCase().charAt(0) % BUCKET_COUNT;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
*/
public class Vector {

private Element[] elements = new Element[100];
private static final int INITIAL_CAPACITY = 100;

private Element[] elements = new Element[INITIAL_CAPACITY];
private int totalElements = 0;

/** Creates an empty vector with capacity 100. */
Expand All @@ -21,26 +23,26 @@ public Vector() {}
* @param element the element to append
*/
public void add(final Element element) {
this.guaranteesSpace();
this.ensureCapacity();

this.elements[this.totalElements] = element;
this.totalElements++;
}

/**
* Inserts an element at an occupied position, shifting it and later elements one position back. Only occupied
* positions are accepted, so this cannot append (use {@link #add(Element)}) or insert into an empty vector.
* Inserts an element at the given position, shifting it and later elements one position back. Position
* {@link #size()} appends.
*
* @param position the zero-based position, from {@code 0} to {@code size() - 1}
* @param position the zero-based position, from {@code 0} to {@link #size()}
* @param element the element to insert
* @throws IllegalArgumentException if {@code position} is negative or not less than {@link #size()}
* @throws IllegalArgumentException if {@code position} is negative or greater than {@link #size()}
*/
public void add(final int position, final Element element) {
if (!isPositionValid(position)) {
throw new IllegalArgumentException("Invalid position");
}

this.guaranteesSpace();
this.ensureCapacity();
for (int i = this.totalElements - 1; i >= position; i--) {
this.elements[i + 1] = this.elements[i];
}
Expand All @@ -49,7 +51,7 @@ public void add(final int position, final Element element) {
}

private boolean isPositionValid(final int position) {
return position >= 0 && position < totalElements;
return position >= 0 && position <= totalElements;
}

/**
Expand All @@ -72,13 +74,14 @@ private boolean positionOccupied(final int position) {

/**
* Removes the element at the given position, shifting later elements one position forward.
* <p>
* The position is not validated: a position not less than {@link #size()} removes the last element instead, and
* a negative position, or any call on an empty vector, fails with an {@code ArrayIndexOutOfBoundsException}.
*
* @param position the zero-based position, from {@code 0} to {@code size() - 1}
* @throws IllegalArgumentException if {@code position} is negative or not less than {@link #size()}
*/
public void remove(final int position) {
if (!positionOccupied(position)) {
throw new IllegalArgumentException("Invalid position");
}
for (int i = position; i < this.totalElements - 1; i++) {
this.elements[i] = this.elements[i + 1];
}
Expand Down Expand Up @@ -112,7 +115,7 @@ public int size() {
return this.totalElements;
}

private void guaranteesSpace() {
private void ensureCapacity() {
if (this.totalElements == this.elements.length) {
final Element[] newElements = new Element[this.elements.length * 2];
for (int i = 0; i < this.elements.length; i++) {
Expand Down
Loading
Loading