diff --git a/.gitignore b/.gitignore index 1611de9..4638a7c 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,4 @@ triage-*.md # jqwik failure database (replays previously failing samples locally) .jqwik-database +findings.md diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/Element.java b/src/main/java/io/github/diegourban/algorithms/datastructure/Element.java index 0ecd9a6..cefff29 100644 --- a/src/main/java/io/github/diegourban/algorithms/datastructure/Element.java +++ b/src/main/java/io/github/diegourban/algorithms/datastructure/Element.java @@ -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. * diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/array/ArrayUtils.java b/src/main/java/io/github/diegourban/algorithms/datastructure/array/ArrayUtils.java index c3c2c83..682caf6 100644 --- a/src/main/java/io/github/diegourban/algorithms/datastructure/array/ArrayUtils.java +++ b/src/main/java/io/github/diegourban/algorithms/datastructure/array/ArrayUtils.java @@ -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. *
* 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. @@ -22,33 +22,33 @@ private ArrayUtils() {} *
* 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; } } diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedList.java b/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedList.java index 9600a71..4e16c94 100644 --- a/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedList.java +++ b/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedList.java @@ -55,17 +55,16 @@ public void addAtEnd(final Element element) { /** * Inserts an element at the given position, shifting later elements one position back. *
- * 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); @@ -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++) { @@ -124,7 +123,7 @@ 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(); @@ -132,14 +131,20 @@ public void removeFromStart() { 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 { @@ -155,14 +160,10 @@ public void removeFromEnd() { *
* 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}. - *
- * 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) {
@@ -170,6 +171,9 @@ public void remove(final int position) {
} 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();
diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/set/Set.java b/src/main/java/io/github/diegourban/algorithms/datastructure/set/Set.java
index 1306ab4..674d9e5 100644
--- a/src/main/java/io/github/diegourban/algorithms/datastructure/set/Set.java
+++ b/src/main/java/io/github/diegourban/algorithms/datastructure/set/Set.java
@@ -10,20 +10,22 @@
*/
public class Set {
+ private static final int BUCKET_COUNT = 26;
+
private final List
- * 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];
}
@@ -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++) {
diff --git a/src/main/java/io/github/diegourban/algorithms/io/Reader.java b/src/main/java/io/github/diegourban/algorithms/io/Reader.java
index a78e850..af6f308 100644
--- a/src/main/java/io/github/diegourban/algorithms/io/Reader.java
+++ b/src/main/java/io/github/diegourban/algorithms/io/Reader.java
@@ -20,8 +20,8 @@
*/
public class Reader {
- private final int BUFFER_SIZE = 1 << 16;
- private DataInputStream din;
+ private static final int BUFFER_SIZE = 1 << 16;
+ private DataInputStream input;
private final byte[] buffer;
private int bufferPointer, bytesRead;
@@ -31,7 +31,7 @@ public class Reader {
* @param input the stream to read from; it is closed by {@link #close()}
*/
public Reader(final InputStream input) {
- din = new DataInputStream(input);
+ this.input = new DataInputStream(input);
buffer = new byte[BUFFER_SIZE];
bufferPointer = bytesRead = 0;
}
@@ -49,20 +49,20 @@ public Reader(final InputStream input) {
* @throws IOException if the underlying stream fails
*/
public String readLine() throws IOException {
- final byte[] buf = new byte[10000]; // line length
- int cnt = 0;
- int c = 0;
+ final byte[] line = new byte[10000]; // line length
+ int length = 0;
+ int nextByte = 0;
- while ((c = read()) != -1) {
- if (c == '\n' || c == '\r') {
- if (cnt == 0) {
+ while ((nextByte = read()) != -1) {
+ if (nextByte == '\n' || nextByte == '\r') {
+ if (length == 0) {
continue;
}
break;
}
- buf[cnt++] = (byte) c;
+ line[length++] = (byte) nextByte;
}
- return new String(buf, 0, cnt);
+ return new String(line, 0, length);
}
/**
@@ -76,27 +76,27 @@ public String readLine() throws IOException {
* @throws IOException if the underlying stream fails
*/
public int nextInt() throws IOException {
- int ret = 0;
- byte c = read();
+ int result = 0;
+ byte nextByte = read();
- while (c <= ' ') {
- c = read();
+ while (nextByte <= ' ') {
+ nextByte = read();
}
- final boolean neg = (c == '-');
- if (neg) {
- c = read();
+ final boolean negative = (nextByte == '-');
+ if (negative) {
+ nextByte = read();
}
do {
- ret = ret * 10 + c - '0';
- } while ((c = read()) >= '0' && c <= '9');
+ result = result * 10 + nextByte - '0';
+ } while ((nextByte = read()) >= '0' && nextByte <= '9');
- if (neg) {
- return -ret;
+ if (negative) {
+ return -result;
}
- return ret;
+ return result;
}
/**
@@ -107,13 +107,13 @@ public int nextInt() throws IOException {
* @throws IOException if the underlying stream fails
*/
public char nextChar() throws IOException {
- byte c = read();
+ byte nextByte = read();
- while (c <= ' ') {
- c = read();
+ while (nextByte <= ' ') {
+ nextByte = read();
}
- return (char) c;
+ return (char) nextByte;
}
/**
@@ -126,27 +126,27 @@ public char nextChar() throws IOException {
* @throws IOException if the underlying stream fails
*/
public long nextLong() throws IOException {
- long ret = 0;
- byte c = read();
+ long result = 0;
+ byte nextByte = read();
- while (c <= ' ') {
- c = read();
+ while (nextByte <= ' ') {
+ nextByte = read();
}
- final boolean neg = (c == '-');
- if (neg) {
- c = read();
+ final boolean negative = (nextByte == '-');
+ if (negative) {
+ nextByte = read();
}
do {
- ret = ret * 10 + c - '0';
- } while ((c = read()) >= '0' && c <= '9');
+ result = result * 10 + nextByte - '0';
+ } while ((nextByte = read()) >= '0' && nextByte <= '9');
- if (neg) {
- return -ret;
+ if (negative) {
+ return -result;
}
- return ret;
+ return result;
}
/**
@@ -160,34 +160,34 @@ public long nextLong() throws IOException {
* @throws IOException if the underlying stream fails
*/
public double nextDouble() throws IOException {
- double ret = 0;
+ double result = 0;
double div = 1;
- byte c = read();
+ byte nextByte = read();
- while (c <= ' ') {
- c = read();
+ while (nextByte <= ' ') {
+ nextByte = read();
}
- final boolean neg = (c == '-');
- if (neg) {
- c = read();
+ final boolean negative = (nextByte == '-');
+ if (negative) {
+ nextByte = read();
}
do {
- ret = ret * 10 + c - '0';
- } while ((c = read()) >= '0' && c <= '9');
+ result = result * 10 + nextByte - '0';
+ } while ((nextByte = read()) >= '0' && nextByte <= '9');
- if (c == '.') {
- while ((c = read()) >= '0' && c <= '9') {
- ret += (c - '0') / (div *= 10);
+ if (nextByte == '.') {
+ while ((nextByte = read()) >= '0' && nextByte <= '9') {
+ result += (nextByte - '0') / (div *= 10);
}
}
- if (neg) {
- return -ret;
+ if (negative) {
+ return -result;
}
- return ret;
+ return result;
}
/**
@@ -204,7 +204,7 @@ public void skip(final int n) throws IOException {
}
private void fillBuffer() throws IOException {
- bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE);
+ bytesRead = input.read(buffer, bufferPointer = 0, BUFFER_SIZE);
if (isEOF()) {
buffer[0] = -1;
}
@@ -231,9 +231,9 @@ private boolean isEOF() {
* @throws IOException if closing the underlying stream fails
*/
public void close() throws IOException {
- if (din != null) {
- din.close();
- din = null;
+ if (input != null) {
+ input.close();
+ input = null;
}
}
@@ -243,6 +243,6 @@ public void close() throws IOException {
* @return {@code true} if this reader is closed, {@code false} otherwise
*/
public boolean isClosed() {
- return din == null;
+ return input == null;
}
}
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Combinatorics.java b/src/main/java/io/github/diegourban/algorithms/math/Combinatorics.java
index 77efc07..28591fc 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Combinatorics.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Combinatorics.java
@@ -31,21 +31,21 @@ private Combinatorics() {}
*/
public static BigInteger binomialCoefficient(final int n, final int m) {
if (m < 0 || m > n) {
- return new BigInteger("0");
+ return BigInteger.ZERO;
}
if (m == 0 || m == n) {
- return new BigInteger("1");
+ return BigInteger.ONE;
}
if (m == 1 || m == n - 1) {
- return new BigInteger(String.valueOf(n));
+ return BigInteger.valueOf(n);
}
final BigInteger[] binomials = new BigInteger[n + 1];
- binomials[0] = new BigInteger("1");
+ binomials[0] = BigInteger.ONE;
for (int i = 1; i <= n; i++) {
- binomials[i] = new BigInteger("1");
+ binomials[i] = BigInteger.ONE;
for (int j = i - 1; j > 0; --j) {
binomials[j] = binomials[j].add(binomials[j - 1]);
}
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Geometry.java b/src/main/java/io/github/diegourban/algorithms/math/Geometry.java
index fd18210..01ea5ad 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Geometry.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Geometry.java
@@ -23,7 +23,6 @@ private Geometry() {}
* @return the distance between the two points
*/
public static double distance(final int x1, final int y1, final int x2, final int y2) {
- // return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
return Math.hypot(x1 - x2, y1 - y2);
}
@@ -115,21 +114,21 @@ public static boolean isValidTriangle(final double a, final double b, final doub
* two are, {@link TriangleType#ESCALENE} otherwise
*/
public static TriangleType checkTriangleType(final double a, final double b, final double c) {
- final double[] array = new double[3];
- array[0] = a;
- array[1] = b;
- array[2] = c;
- Arrays.sort(array);
+ final double[] sortedSides = new double[3];
+ sortedSides[0] = a;
+ sortedSides[1] = b;
+ sortedSides[2] = c;
+ Arrays.sort(sortedSides);
- final double min = array[0];
- final double med = array[1];
- final double max = array[2];
+ final double shortest = sortedSides[0];
+ final double middle = sortedSides[1];
+ final double longest = sortedSides[2];
- if (max == med && med == min) {
+ if (longest == middle && middle == shortest) {
return TriangleType.EQUILATERAL;
}
- if (max == med || med == min) {
+ if (longest == middle || middle == shortest) {
return TriangleType.ISOSCELES;
}
@@ -149,19 +148,19 @@ public static TriangleType checkTriangleType(final double a, final double b, fin
* @return {@code true} if the sides satisfy the Pythagorean theorem, {@code false} otherwise
*/
public static boolean isRectangleTriangle(final double a, final double b, final double c) {
- final double[] array = new double[3];
- array[0] = a;
- array[1] = b;
- array[2] = c;
- Arrays.sort(array);
-
- final double min = array[0];
- final double med = array[1];
- final double max = array[2];
-
- final double maxSquared = max * max;
- final double medSquared = med * med;
- final double minSquared = min * min;
+ final double[] sortedSides = new double[3];
+ sortedSides[0] = a;
+ sortedSides[1] = b;
+ sortedSides[2] = c;
+ Arrays.sort(sortedSides);
+
+ final double shortest = sortedSides[0];
+ final double middle = sortedSides[1];
+ final double longest = sortedSides[2];
+
+ final double maxSquared = longest * longest;
+ final double medSquared = middle * middle;
+ final double minSquared = shortest * shortest;
return maxSquared == medSquared + minSquared;
}
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Measurement.java b/src/main/java/io/github/diegourban/algorithms/math/Measurement.java
index 37f9f2a..8b04267 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Measurement.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Measurement.java
@@ -7,6 +7,9 @@
*/
public final class Measurement {
+ private static final double KILOMETERS_PER_MILE = 1.609344;
+ private static final double MILES_PER_KILOMETER = 0.621371;
+
private Measurement() {}
/**
@@ -16,7 +19,7 @@ private Measurement() {}
* @return the amount of miles converted to kilometers
*/
public static double milesToKilometers(final double miles) {
- return miles * 1.609344;
+ return miles * KILOMETERS_PER_MILE;
}
/**
@@ -26,6 +29,6 @@ public static double milesToKilometers(final double miles) {
* @return the amount of kilometers converted to miles
*/
public static double kilometersToMiles(final double kilometers) {
- return kilometers * 0.621371;
+ return kilometers * MILES_PER_KILOMETER;
}
}
diff --git a/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java b/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java
index 8297bf7..39e8348 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java
@@ -12,16 +12,19 @@ private NumberTheory() {}
/**
* Least common multiple (lcm) of two numbers.
*
- * Computed as {@code a * (b / gcdLoop(a, b))}; intended for positive numbers. The result overflows silently if it
- * does not fit in a {@code long}.
+ * Computed as {@code a * (b / gcdLoop(a, b))}; intended for non-negative numbers. If either number is {@code 0}
+ * the result is {@code 0}.
*
* @param a the first number
* @param b the second number
* @return The least common multiple of two numbers.
- * @throws ArithmeticException if both {@code a} and {@code b} are zero
+ * @throws ArithmeticException if the result does not fit in a {@code long}
*/
public static long lcm(final long a, final long b) {
- return a * (b / gcdLoop(a, b));
+ if (a == 0 || b == 0) {
+ return 0;
+ }
+ return Math.multiplyExact(a, b / gcdLoop(a, b));
}
/**
@@ -39,7 +42,7 @@ public static long lcm(final long[] input) {
}
/**
- * Greatest common divisor (gdc) of two numbers using while strategy.
+ * Greatest common divisor (gcd) of two numbers using while strategy.
*
* Also known as greatest common factor (gcf), highest common factor (hcf),
* greatest common measure (gcm) or highest common divisor
@@ -52,18 +55,18 @@ public static long lcm(final long[] input) {
* @return The greatest common divisor of two numbers.
*/
public static long gcdLoop(final long a, final long b) {
- long gcd = a;
- long bNumber = b;
- while (bNumber > 0) {
- final long temp = bNumber;
- bNumber = gcd % bNumber; // % is remainder
- gcd = temp;
+ long dividend = a;
+ long divisor = b;
+ while (divisor > 0) {
+ final long temp = divisor;
+ divisor = dividend % divisor;
+ dividend = temp;
}
- return gcd;
+ return dividend;
}
/**
- * Greatest common divisor (gdc) of two numbers using recursive strategy.
+ * Greatest common divisor (gcd) of two numbers using recursive strategy.
*
* Also known as greatest common factor (gcf), highest common factor (hcf),
* greatest common measure (gcm) or highest common divisor
@@ -98,6 +101,7 @@ public static boolean isPrime(final int n) {
return false;
}
+ // every prime above 3 has the form 6k ± 1, so only i and i + 2 need checking per step of 6
int i = 5;
while (i <= n / i) {
if (n % i == 0 || n % (i + 2) == 0) {
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Numbers.java b/src/main/java/io/github/diegourban/algorithms/math/Numbers.java
index 50f5268..36749be 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Numbers.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Numbers.java
@@ -10,23 +10,25 @@ private Numbers() {}
/**
* Checks if a number contains a digit
*
- * The sign of {@code number} is ignored. The number {@code 0} is treated as having no digits, so
- * {@code containsDigit(0, 0)} returns {@code false}.
+ * The sign of {@code number} is ignored; {@code 0} contains the digit {@code 0}.
*
* @param number the number to check
* @param digit the digit expected to find in the number
* @return {@code true} if the number contains the digit, {@code false} otherwise
*/
public static boolean containsDigit(final long number, final long digit) {
- long thisNumber = number >= 0 ? number : -number;
- long thisDigit;
+ long remaining = number >= 0 ? number : -number;
+ long lastDigit;
- while (thisNumber != 0) {
- thisDigit = thisNumber % 10; // Always equal to the last digit of thisNumber
- thisNumber = thisNumber
- / 10; // Always equal to thisNumber with the last digit chopped off, or 0 if thisNumber is less than
- // 10
- if (thisDigit == digit) {
+ if (remaining == 0) {
+ return digit == 0;
+ }
+
+ // take the last digit, then chop it off, until no digits are left
+ while (remaining != 0) {
+ lastDigit = remaining % 10;
+ remaining = remaining / 10;
+ if (lastDigit == digit) {
return true;
}
}
@@ -46,20 +48,20 @@ public static boolean containsDigit(final long number, final long digit) {
* times, {@code false} otherwise
*/
public static boolean containsRepeatingDigits(final long number, final int times) {
- long theNumber = number;
- Long old = null;
- int reps = 0;
+ long remaining = number;
+ Long previousDigit = null;
+ int runLength = 0;
- while (theNumber > 0) {
- final Long n = theNumber % 10;
- theNumber /= 10;
- if (n.equals(old)) {
- if (++reps == times) {
+ while (remaining > 0) {
+ final Long digit = remaining % 10;
+ remaining /= 10;
+ if (digit.equals(previousDigit)) {
+ if (++runLength == times) {
return true;
}
} else {
- old = n;
- reps = 1;
+ previousDigit = digit;
+ runLength = 1;
}
}
@@ -70,9 +72,7 @@ public static boolean containsRepeatingDigits(final long number, final int times
* Converts a number to an array of its digits, least significant digit first: {@code 123} becomes
* {@code [3, 2, 1]}.
*
- * Intended for non-negative numbers that fit in an {@code int}: each step truncates the number to {@code int}
- * before taking the last digit, so larger values give wrong digits, and a negative number gives negative digits
- * plus an extra trailing {@code 0} for the minus sign.
+ * A negative number gives the digits of its absolute value: {@code -12} becomes {@code [2, 1]}.
*
* @param number the number to convert
* @return an array with each digit of the number, in reverse order
@@ -80,11 +80,11 @@ public static boolean containsRepeatingDigits(final long number, final int times
public static int[] toArray(final long number) {
long theNumber = number;
- final int length = Long.toString(theNumber).length();
+ final int length = Long.toString(theNumber).length() - (theNumber < 0 ? 1 : 0);
final int[] array = new int[length];
for (int index = 0; index < length; index++) {
- array[index] = (int) theNumber % 10;
+ array[index] = Math.abs((int) (theNumber % 10));
theNumber /= 10;
}
@@ -94,12 +94,15 @@ public static int[] toArray(final long number) {
/**
* Find the length of the int using divide and conquer
*
- * Intended for non-negative numbers: any negative number returns 1.
+ * A negative number counts the digits of its absolute value: {@code length(-123)} is {@code 3}.
*
* @param number some number
* @return the number of decimal digits of the number, from 1 to 10
*/
public static int length(final int number) {
+ if (number < 0) {
+ return number == Integer.MIN_VALUE ? 10 : length(-number);
+ }
if (number < 100000) {
// 5 or less
if (number < 100) {
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Sequences.java b/src/main/java/io/github/diegourban/algorithms/math/Sequences.java
index 54f4064..b293ccb 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Sequences.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Sequences.java
@@ -9,6 +9,9 @@
*/
public final class Sequences {
+ private static final double SQRT_5 = Math.sqrt(5);
+ private static final double GOLDEN_RATIO = (1 + SQRT_5) / 2;
+
private Sequences() {}
/**
@@ -24,9 +27,8 @@ private Sequences() {}
* @throws IllegalArgumentException if {@code n} is negative
*/
public static int fibonacci(final int n) {
- // edge cases:
if (n < 0) {
- throw new IllegalArgumentException("Index was negative. No such thing as a negative index in a series.");
+ throw new IllegalArgumentException("n must be non-negative: " + n);
} else if (n == 0 || n == 1) {
return n;
}
@@ -35,7 +37,7 @@ public static int fibonacci(final int n) {
// so we'll need to track the previous 2 numbers at each step
int prevPrev = 0; // 0th fibonacci
int prev = 1; // 1st fibonacci
- int current = 0; // Declare and initialize current
+ int current = 0;
for (int i = 1; i < n; i++) {
@@ -53,11 +55,11 @@ public static int fibonacci(final int n) {
/**
* Calculates the nth Fibonacci number.
- * Works with n <= 67
+ * Works with n <= 70
*
* Uses Binet's formula in {@code double} arithmetic, so the result is approximate (for example
* {@code 55.000000000000014} for n = 10). Rounded with {@link Math#round(double)}, it equals the exact value for
- * 0 <= n <= 67. Negative {@code n} is not rejected and gives a meaningless result.
+ * 0 <= n <= 70. Negative {@code n} is not rejected and gives a meaningless result.
*
* @param n the nth number in the Fibonacci sequence to calculate
* @return an approximation of the nth Fibonacci number
@@ -66,9 +68,7 @@ public static double fibonacciSimpleBinetFormula(final int n) {
if (n == 0) {
return 0;
}
- final double phi = (1 + Math.sqrt(5)) / 2;
- final double sqrt5 = Math.sqrt(5);
- return ((Math.pow(phi, n) - Math.pow(-phi, -n)) / sqrt5);
+ return ((Math.pow(GOLDEN_RATIO, n) - Math.pow(-GOLDEN_RATIO, -n)) / SQRT_5);
}
/**
@@ -84,20 +84,19 @@ public static double fibonacciSimpleBinetFormula(final int n) {
*/
public static BigInteger fibonacciBinetFormula(final int n) {
if (n == 0) {
- return new BigInteger("0");
+ return BigInteger.ZERO;
}
if (n == 1 || n == 2) {
- return new BigInteger("1");
+ return BigInteger.ONE;
}
if (n == 3) {
- return new BigInteger("2");
+ return BigInteger.TWO;
}
- final double phi = (1 + Math.sqrt(5)) / 2;
- final BigDecimal sqrt5 = BigDecimal.valueOf(Math.sqrt(5));
+ final BigDecimal sqrt5 = BigDecimal.valueOf(SQRT_5);
- final BigDecimal pow = BigDecimal.valueOf(Math.pow(phi, n));
- final BigDecimal powInvrt = BigDecimal.valueOf(Math.pow(-phi, -n));
+ final BigDecimal pow = BigDecimal.valueOf(Math.pow(GOLDEN_RATIO, n));
+ final BigDecimal powInvrt = BigDecimal.valueOf(Math.pow(-GOLDEN_RATIO, -n));
return pow.subtract(powInvrt).divide(sqrt5, RoundingMode.HALF_UP).toBigInteger();
}
}
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Statistics.java b/src/main/java/io/github/diegourban/algorithms/math/Statistics.java
index d852c62..b90c1ab 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Statistics.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Statistics.java
@@ -37,9 +37,10 @@ public static double mean(final double[] data) {
*
* @param data the values; must not be empty, and is sorted ascending as a side effect
* @return the middle value for an odd number of values, or the mean of the two middle values for an even number
- * @throws ArrayIndexOutOfBoundsException if {@code data} is empty
+ * @throws IllegalArgumentException if {@code data} is empty
*/
public static double median(final double[] data) {
+ requireNotEmpty(data);
Arrays.sort(data);
final int middle = data.length / 2;
@@ -84,9 +85,11 @@ public static double mode(final double[] data) {
* {@linkplain #mean(double[]) mean}.
*
* @param data the values
- * @return the Average Absolute Deviation from data; {@code NaN} if {@code data} is empty
+ * @return the Average Absolute Deviation from data
+ * @throws IllegalArgumentException if {@code data} is empty
*/
public static double meanAbsoluteDeviation(final double[] data) {
+ requireNotEmpty(data);
final double mean = mean(data);
double sum = 0;
@@ -96,4 +99,10 @@ public static double meanAbsoluteDeviation(final double[] data) {
return sum / data.length;
}
+
+ private static void requireNotEmpty(final double[] data) {
+ if (data.length == 0) {
+ throw new IllegalArgumentException("data must not be empty");
+ }
+ }
}
diff --git a/src/main/java/io/github/diegourban/algorithms/math/TriangleType.java b/src/main/java/io/github/diegourban/algorithms/math/TriangleType.java
index e7df295..f29711b 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/TriangleType.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/TriangleType.java
@@ -6,11 +6,11 @@
*/
public enum TriangleType {
/** All three sides are equal. */
- EQUILATERAL("Equilatero"),
+ EQUILATERAL("Equilateral"),
/** No two sides are equal (scalene). */
- ESCALENE("Escaleno"),
+ ESCALENE("Scalene"),
/** Exactly two sides are equal. */
- ISOSCELES("Isoceles");
+ ISOSCELES("Isosceles");
private final String name;
@@ -21,7 +21,7 @@ private TriangleType(final String name) {
/**
* Returns the display name of this type, in Portuguese.
*
- * @return the display name, such as {@code "Equilatero"}
+ * @return the display name, such as {@code "Equilateral"}
*/
public String getName() {
return name;
diff --git a/src/main/java/io/github/diegourban/algorithms/search/BinarySearch.java b/src/main/java/io/github/diegourban/algorithms/search/BinarySearch.java
index 7f4abe2..e95e64b 100644
--- a/src/main/java/io/github/diegourban/algorithms/search/BinarySearch.java
+++ b/src/main/java/io/github/diegourban/algorithms/search/BinarySearch.java
@@ -21,15 +21,15 @@ public static int indexOf(final int[] sorted, final int key) {
/**
* Finds an element in a list
*
- * Complexity: O(log n) time and O(1) space
+ * Complexity: O(log n) time and O(log n) space for the recursion
*
* @param items an ordered list of items to find
* @param from the index to find from in the list
* @param to the index to find to in the list
- * @param element the element to find
+ * @param key the element to find
* @return the index of the element, -1 otherwise
*/
- private static int find(final int[] items, final int from, final int to, final int element) {
+ private static int find(final int[] items, final int from, final int to, final int key) {
if (from > to) {
return -1;
}
@@ -37,12 +37,12 @@ private static int find(final int[] items, final int from, final int to, final i
final int middle = (from + to) / 2;
final int middleItem = items[middle];
- if (element == middleItem) {
+ if (key == middleItem) {
return middle;
}
- if (element < middleItem) {
- return find(items, from, middle - 1, element);
+ if (key < middleItem) {
+ return find(items, from, middle - 1, key);
}
- return find(items, middle + 1, to, element);
+ return find(items, middle + 1, to, key);
}
}
diff --git a/src/main/java/io/github/diegourban/algorithms/sorting/InsertionSort.java b/src/main/java/io/github/diegourban/algorithms/sorting/InsertionSort.java
index 01ccbd6..dd2475e 100644
--- a/src/main/java/io/github/diegourban/algorithms/sorting/InsertionSort.java
+++ b/src/main/java/io/github/diegourban/algorithms/sorting/InsertionSort.java
@@ -22,14 +22,14 @@ public void sort(final int[] items) {
*/
private void sort(final int[] items, final int totalElements) {
for (int current = 1; current < totalElements; current++) {
- int analysis = current;
+ int position = current;
- while (analysis > 0 && items[analysis] < items[analysis - 1]) {
- final Integer analysisItem = items[analysis];
- final Integer previousItem = items[analysis - 1];
- items[analysis] = previousItem;
- items[analysis - 1] = analysisItem;
- analysis--;
+ while (position > 0 && items[position] < items[position - 1]) {
+ final Integer itemBeingInserted = items[position];
+ final Integer previousItem = items[position - 1];
+ items[position] = previousItem;
+ items[position - 1] = itemBeingInserted;
+ position--;
}
}
}
diff --git a/src/main/java/io/github/diegourban/algorithms/sorting/MergeSort.java b/src/main/java/io/github/diegourban/algorithms/sorting/MergeSort.java
index 3ee1e38..a4824fd 100644
--- a/src/main/java/io/github/diegourban/algorithms/sorting/MergeSort.java
+++ b/src/main/java/io/github/diegourban/algorithms/sorting/MergeSort.java
@@ -33,40 +33,40 @@ private void sort(final int[] items, final int begin, final int end) {
private void merge(final int[] items, final int begin, final int middle, final int end) {
final int[] result = new int[end - begin];
- int current = 0;
+ int resultIndex = 0;
int currentLeft = begin;
int currentRight = middle;
while (currentLeft < middle && currentRight < end) {
- final int firstValue = items[currentLeft];
- final int secondValue = items[currentRight];
+ final int leftValue = items[currentLeft];
+ final int rightValue = items[currentRight];
- if (firstValue < secondValue) {
- result[current] = firstValue;
+ if (leftValue < rightValue) {
+ result[resultIndex] = leftValue;
currentLeft++;
} else {
- result[current] = secondValue;
+ result[resultIndex] = rightValue;
currentRight++;
}
- current++;
+ resultIndex++;
}
// remaining values on the left side
while (currentLeft < middle) {
- result[current] = items[currentLeft];
+ result[resultIndex] = items[currentLeft];
currentLeft++;
- current++;
+ resultIndex++;
}
// remaining values on the right side
while (currentRight < end) {
- result[current] = items[currentRight];
+ result[resultIndex] = items[currentRight];
currentRight++;
- current++;
+ resultIndex++;
}
- for (int count = 0; count < current; count++) {
+ for (int count = 0; count < resultIndex; count++) {
items[begin + count] = result[count];
}
}
diff --git a/src/main/java/io/github/diegourban/algorithms/sorting/QuickSort.java b/src/main/java/io/github/diegourban/algorithms/sorting/QuickSort.java
index 9ebf87d..ed07426 100644
--- a/src/main/java/io/github/diegourban/algorithms/sorting/QuickSort.java
+++ b/src/main/java/io/github/diegourban/algorithms/sorting/QuickSort.java
@@ -32,18 +32,18 @@ private void sort(final int[] items, final int start, final int end) {
}
private int partition(final int[] items, final int start, final int end) {
- int minorsFound = start;
+ int boundary = start;
final int pivot = items[end - 1];
for (int current = start; current < end - 1; current++) {
final int currentItem = items[current];
if (currentItem <= pivot) {
- swap(items, current, minorsFound);
- minorsFound++;
+ swap(items, current, boundary);
+ boundary++;
}
}
- swap(items, end - 1, minorsFound);
- return minorsFound;
+ swap(items, end - 1, boundary);
+ return boundary;
}
private void swap(final int[] items, final int from, final int to) {
diff --git a/src/main/java/io/github/diegourban/algorithms/sorting/SelectionSort.java b/src/main/java/io/github/diegourban/algorithms/sorting/SelectionSort.java
index 11f8ba8..68c1bf1 100644
--- a/src/main/java/io/github/diegourban/algorithms/sorting/SelectionSort.java
+++ b/src/main/java/io/github/diegourban/algorithms/sorting/SelectionSort.java
@@ -22,21 +22,21 @@ public void sort(final int[] items) {
*/
private void sort(final int[] items, final int totalElements) {
for (int current = 0; current < totalElements - 1; current++) {
- final int smallerIndex = findSmallerIndex(items, current, totalElements);
+ final int smallestIndex = findSmallestIndex(items, current, totalElements);
final Integer currentItem = items[current];
- final Integer smallerItem = items[smallerIndex];
- items[current] = smallerItem;
- items[smallerIndex] = currentItem;
+ final Integer smallestItem = items[smallestIndex];
+ items[current] = smallestItem;
+ items[smallestIndex] = currentItem;
}
}
- private int findSmallerIndex(final int[] items, final int begin, final int end) {
- int smallerIndex = begin;
+ private int findSmallestIndex(final int[] items, final int begin, final int end) {
+ int smallestIndex = begin;
for (int i = begin; i < end; i++) {
- if (items[i] < items[smallerIndex]) {
- smallerIndex = i;
+ if (items[i] < items[smallestIndex]) {
+ smallestIndex = i;
}
}
- return smallerIndex;
+ return smallestIndex;
}
}
diff --git a/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java b/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java
index 8db1e12..b154335 100644
--- a/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java
+++ b/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java
@@ -15,23 +15,23 @@ private StringUtils() {}
* The comparison is exact, character by character: case, spaces and punctuation all count, so {@code "Aba"} is
* not a palindrome. Runs in O(n) time.
*
- * @param theString the string to check
+ * @param text the string to check
* @return {@code true} if the string is a palindrome; {@code false} otherwise, including for {@code null} and the
* empty string
*/
- public static boolean isPalindrome(final String theString) {
- if (theString == null || theString.isEmpty()) {
+ public static boolean isPalindrome(final String text) {
+ if (text == null || text.isEmpty()) {
return false;
}
- if (theString.length() == 1) {
+ if (text.length() == 1) {
return true;
}
- for (int i = 0; i < theString.length() / 2; i++) {
- final char c1 = theString.charAt(i);
- final char c2 = theString.charAt(theString.length() - i - 1);
- if (c1 != c2) {
+ for (int i = 0; i < text.length() / 2; i++) {
+ final char fromStart = text.charAt(i);
+ final char fromEnd = text.charAt(text.length() - i - 1);
+ if (fromStart != fromEnd) {
return false;
}
}
@@ -41,8 +41,8 @@ public static boolean isPalindrome(final String theString) {
/**
* Checks if two strings are anagrams: the same characters, each the same number of times, in any order.
*
- * The comparison ignores case (both strings are lower-cased first) but counts spaces and punctuation. Runs in
- * O(n log n) time by sorting the characters of both strings.
+ * The comparison ignores case (both strings are lower-cased first, independently of the default locale) but
+ * counts spaces and punctuation. Runs in O(n log n) time by sorting the characters of both strings.
*
* @param a the first string; must not be {@code null}
* @param b the second string; must not be {@code null}
@@ -54,13 +54,13 @@ public static boolean isAnagram(final String a, final String b) {
return false;
}
- final char[] arrA = sort(a);
- final char[] arrB = sort(b);
- return Arrays.equals(arrA, arrB);
+ final char[] sortedA = sortedLowerCaseChars(a);
+ final char[] sortedB = sortedLowerCaseChars(b);
+ return Arrays.equals(sortedA, sortedB);
}
- private static char[] sort(final String original) {
- final char[] chars = original.toLowerCase().toCharArray();
+ private static char[] sortedLowerCaseChars(final String text) {
+ final char[] chars = text.toLowerCase(Locale.ROOT).toCharArray();
Arrays.sort(chars); // Dual-Pivot Quicksort, O(n log(n))
return chars;
}
diff --git a/src/test/java/io/github/diegourban/algorithms/datastructure/ElementTest.java b/src/test/java/io/github/diegourban/algorithms/datastructure/ElementTest.java
index bd7c4ea..369551e 100644
--- a/src/test/java/io/github/diegourban/algorithms/datastructure/ElementTest.java
+++ b/src/test/java/io/github/diegourban/algorithms/datastructure/ElementTest.java
@@ -21,4 +21,16 @@ void shouldNotEqualNull() {
void shouldPrintItsName() {
assertThat(new Element("diego")).hasToString("diego");
}
+
+ @Test
+ void shouldNotEqualAnObjectOfAnotherType() {
+ assertThat(new Element("diego")).isNotEqualTo("diego");
+ }
+
+ @Test
+ void shouldHashEqualElementsAlike() {
+ assertThat(new Element("diego")).hasSameHashCodeAs(new Element("diego"));
+ assertThat(new java.util.HashSet<>(java.util.List.of(new Element("diego"))))
+ .contains(new Element("diego"));
+ }
}
diff --git a/src/test/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedListTest.java b/src/test/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedListTest.java
index e2a6192..ba006fa 100644
--- a/src/test/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedListTest.java
+++ b/src/test/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedListTest.java
@@ -195,4 +195,41 @@ void shouldRemoveFirstAndLastByPosition() {
void shouldPrintEmptyList() {
assertThat(new LinkedList()).hasToString("[]");
}
+
+ @Test
+ void shouldAddAtPositionZeroOfNonEmptyList() {
+ final LinkedList linkedList = new LinkedList();
+ linkedList.addAtEnd(new Element("paulo"));
+ linkedList.addAtEnd(new Element("josé"));
+
+ linkedList.add(0, new Element("diego"));
+
+ assertThat(linkedList).hasToString("[diego,paulo,josé,]");
+ assertThat(linkedList.getFirst().getNext().getPrevious().getElement()).isEqualTo(new Element("diego"));
+ }
+
+ @Test
+ void shouldRejectRemovingFromEndOfEmptyList() {
+ assertThatIllegalStateException().isThrownBy(() -> new LinkedList().removeFromEnd());
+ }
+
+ @Test
+ void shouldRejectRemovingAMissingPosition() {
+ final LinkedList linkedList = new LinkedList();
+ linkedList.addAtEnd(new Element("diego"));
+
+ assertThatIllegalArgumentException().isThrownBy(() -> linkedList.remove(1));
+ assertThat(linkedList.size()).isEqualTo(1);
+ }
+
+ @Test
+ void shouldUnlinkTheRemovedNodeFromTheNewFirst() {
+ final LinkedList linkedList = new LinkedList();
+ linkedList.addAtEnd(new Element("diego"));
+ linkedList.addAtEnd(new Element("paulo"));
+
+ linkedList.removeFromStart();
+
+ assertThat(linkedList.getFirst().getPrevious()).isNull();
+ }
}
diff --git a/src/test/java/io/github/diegourban/algorithms/datastructure/set/SetTest.java b/src/test/java/io/github/diegourban/algorithms/datastructure/set/SetTest.java
index 001dc4d..0e22333 100644
--- a/src/test/java/io/github/diegourban/algorithms/datastructure/set/SetTest.java
+++ b/src/test/java/io/github/diegourban/algorithms/datastructure/set/SetTest.java
@@ -1,6 +1,7 @@
package io.github.diegourban.algorithms.datastructure.set;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import org.junit.jupiter.api.Test;
@@ -53,4 +54,12 @@ void shouldIgnoreRemovingAMissingWord() {
.hasToString(
"[[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [Diego], [], [], []]");
}
+
+ @Test
+ void shouldRejectAnEmptyWord() {
+ final Set set = new Set();
+
+ assertThatIllegalArgumentException().isThrownBy(() -> set.add("")).withMessage("word must not be empty");
+ assertThatIllegalArgumentException().isThrownBy(() -> set.remove("")).withMessage("word must not be empty");
+ }
}
diff --git a/src/test/java/io/github/diegourban/algorithms/datastructure/vector/VectorTest.java b/src/test/java/io/github/diegourban/algorithms/datastructure/vector/VectorTest.java
index e5a21e4..bd883a6 100644
--- a/src/test/java/io/github/diegourban/algorithms/datastructure/vector/VectorTest.java
+++ b/src/test/java/io/github/diegourban/algorithms/datastructure/vector/VectorTest.java
@@ -73,4 +73,44 @@ void shouldRemoveFromAFullVector() {
assertThat(vector.size()).isEqualTo(99);
assertThat(vector.get(0)).isEqualTo(new Element("Element 1"));
}
+
+ @Test
+ void shouldAddAtPositionEqualToSizeAsLast() {
+ final Vector vector = new Vector();
+ vector.add(new Element("Joao"));
+
+ vector.add(1, new Element("Jose"));
+
+ assertThat(vector.size()).isEqualTo(2);
+ assertThat(vector.get(1)).isEqualTo(new Element("Jose"));
+ }
+
+ @Test
+ void shouldAddAtPositionZeroOfEmptyVector() {
+ final Vector vector = new Vector();
+
+ vector.add(0, new Element("Joao"));
+
+ assertThat(vector.size()).isEqualTo(1);
+ assertThat(vector.get(0)).isEqualTo(new Element("Joao"));
+ }
+
+ @Test
+ void shouldRejectRemovingAMissingPosition() {
+ final Vector vector = new Vector();
+ vector.add(new Element("Joao"));
+ vector.add(new Element("Jose"));
+
+ assertThatIllegalArgumentException().isThrownBy(() -> vector.remove(5));
+ assertThatIllegalArgumentException().isThrownBy(() -> vector.remove(-1));
+ assertThat(vector.size()).isEqualTo(2);
+ }
+
+ @Test
+ void shouldRejectRemovingFromEmptyVector() {
+ final Vector vector = new Vector();
+
+ assertThatIllegalArgumentException().isThrownBy(() -> vector.remove(0));
+ assertThat(vector.size()).isZero();
+ }
}
diff --git a/src/test/java/io/github/diegourban/algorithms/io/ReaderTest.java b/src/test/java/io/github/diegourban/algorithms/io/ReaderTest.java
index 380ff49..ebaaa9b 100644
--- a/src/test/java/io/github/diegourban/algorithms/io/ReaderTest.java
+++ b/src/test/java/io/github/diegourban/algorithms/io/ReaderTest.java
@@ -499,4 +499,13 @@ void shouldSkipNChars() throws Exception {
assertThat(day).isEqualTo(5);
}
+
+ @Test
+ void shouldEndLinesAtCarriageReturnsAndSkipBlankOnes() throws Exception {
+ final Reader reader = new Reader(new java.io.ByteArrayInputStream("first\r\nsecond\r\r\nthird".getBytes()));
+
+ assertThat(reader.readLine()).isEqualTo("first");
+ assertThat(reader.readLine()).isEqualTo("second");
+ assertThat(reader.readLine()).isEqualTo("third");
+ }
}
diff --git a/src/test/java/io/github/diegourban/algorithms/math/NumberTheoryTest.java b/src/test/java/io/github/diegourban/algorithms/math/NumberTheoryTest.java
index 6bb46af..db2d170 100644
--- a/src/test/java/io/github/diegourban/algorithms/math/NumberTheoryTest.java
+++ b/src/test/java/io/github/diegourban/algorithms/math/NumberTheoryTest.java
@@ -1,6 +1,7 @@
package io.github.diegourban.algorithms.math;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.List;
import java.util.Set;
@@ -162,4 +163,17 @@ void shouldCreateSetOfPrimeFactorsOfPrimeNumber() {
final Set