From 1bdcee5a9afc4194dad07228342d7c2ec8b2765c Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:17:12 +0100 Subject: [PATCH 01/10] Fix LinkedList add/remove edge cases (B1-B4) - add(0, e) on a non-empty list now inserts at the start instead of throwing IllegalArgumentException - removeFromEnd() on an empty list throws IllegalStateException, like removeFromStart(), instead of NullPointerException - remove(position) rejects an unoccupied position with IllegalArgumentException instead of NullPointerException - removeFromStart() clears the new first node's previous link Each case has a test that failed before the fix. Co-Authored-By: Claude Opus 5.5 --- .../datastructure/linkedlist/LinkedList.java | 34 +++++++++-------- .../linkedlist/LinkedListTest.java | 37 +++++++++++++++++++ 2 files changed, 56 insertions(+), 15 deletions(-) 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..12dc7af 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); @@ -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("Lista vazia"); + } 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("Posição inexistente"); + } final LinkedListNode previous = this.getNode(position - 1); final LinkedListNode current = previous.getNext(); final LinkedListNode next = current.getNext(); 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(); + } } From f0deabf819b40e1e1bbc912c56b75131fc39677a Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:17:40 +0100 Subject: [PATCH 02/10] Fix Vector insert-at-end and unchecked remove (B6-B9) - add(position, e) accepts position == size(), so it can append and insert into an empty vector - remove(position) rejects an unoccupied position with IllegalArgumentException; before, a position past the end silently removed the last element and an empty vector's size dropped to -1 Each case has a test that failed before the fix. Co-Authored-By: Claude Opus 5.5 --- .../datastructure/vector/Vector.java | 17 ++++---- .../datastructure/vector/VectorTest.java | 40 +++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java b/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java index 828aceb..60c6eec 100644 --- a/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java +++ b/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java @@ -28,12 +28,12 @@ public void add(final Element element) { } /** - * 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)) { @@ -49,7 +49,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; } /** @@ -72,13 +72,14 @@ private boolean positionOccupied(final int position) { /** * Removes the element at the given position, shifting later elements one position forward. - *

- * 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]; } 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(); + } } From 92b440a98babb46518f7c7e295be5d076858f9ed Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:18:21 +0100 Subject: [PATCH 03/10] Fix Element equals/hashCode and Set empty-word handling (B11-B13) - Element.equals returns false for objects of other types instead of throwing ClassCastException - Element.hashCode is now overridden from the name, so equal elements are found in hash-based collections - Set.add/remove reject an empty word with IllegalArgumentException instead of StringIndexOutOfBoundsException Each case has a test that failed before the fix. Co-Authored-By: Claude Opus 5.5 --- .../algorithms/datastructure/Element.java | 22 +++++++++++++------ .../algorithms/datastructure/set/Set.java | 11 ++++++---- .../algorithms/datastructure/ElementTest.java | 12 ++++++++++ .../algorithms/datastructure/set/SetTest.java | 9 ++++++++ 4 files changed, 43 insertions(+), 11 deletions(-) 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/set/Set.java b/src/main/java/io/github/diegourban/algorithms/datastructure/set/Set.java index 1306ab4..712d795 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 @@ -20,10 +20,10 @@ public Set() { } /** - * 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)) { @@ -34,10 +34,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)) { @@ -53,6 +53,9 @@ private boolean contains(final String word) { } private int calculateTableIndex(final String word) { + if (word.isEmpty()) { + throw new IllegalArgumentException("word must not be empty"); + } return word.toLowerCase().charAt(0) % 26; } 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/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"); + } } From 03b59b6752f63cc132d86fd25351039be31df81a Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:18:59 +0100 Subject: [PATCH 04/10] Fix Numbers digit handling for large, negative and zero values (B14-B17) - toArray casts after taking % 10, so numbers beyond the int range give the right digits (12345678901 was [-7, 0, 9, ...]) - toArray of a negative number gives the digits of its absolute value ([2, 1] for -12, was [-2, -1, 0]) - containsDigit(0, 0) is true - length(negative) counts the digits of the absolute value (was 1) Each case has a test that failed before the fix. Co-Authored-By: Claude Opus 5.5 --- .../diegourban/algorithms/math/Numbers.java | 20 +++++++++------ .../algorithms/math/NumbersTest.java | 25 +++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) 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..105eb04 100644 --- a/src/main/java/io/github/diegourban/algorithms/math/Numbers.java +++ b/src/main/java/io/github/diegourban/algorithms/math/Numbers.java @@ -10,8 +10,7 @@ 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 @@ -21,6 +20,10 @@ public static boolean containsDigit(final long number, final long digit) { long thisNumber = number >= 0 ? number : -number; long thisDigit; + if (thisNumber == 0) { + return digit == 0; + } + while (thisNumber != 0) { thisDigit = thisNumber % 10; // Always equal to the last digit of thisNumber thisNumber = thisNumber @@ -70,9 +73,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 +81,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 +95,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/test/java/io/github/diegourban/algorithms/math/NumbersTest.java b/src/test/java/io/github/diegourban/algorithms/math/NumbersTest.java index fc8aba5..38ab033 100644 --- a/src/test/java/io/github/diegourban/algorithms/math/NumbersTest.java +++ b/src/test/java/io/github/diegourban/algorithms/math/NumbersTest.java @@ -38,4 +38,29 @@ void shouldCalculateNumberLength() { assertThat(Numbers.length(123456789)).isEqualTo(9); assertThat(Numbers.length(1234567890)).isEqualTo(10); } + + @Test + void shouldSplitNumbersBeyondTheIntRangeIntoDigits() { + assertThat(Numbers.toArray(12345678901L)).containsExactly(1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1); + assertThat(Numbers.toArray(Long.MAX_VALUE)).hasSize(19).startsWith(7, 0, 8); + } + + @Test + void shouldSplitNegativeNumbersIntoTheDigitsOfTheirAbsoluteValue() { + assertThat(Numbers.toArray(-12)).containsExactly(2, 1); + assertThat(Numbers.toArray(Long.MIN_VALUE)).hasSize(19).startsWith(8, 0, 8); + } + + @Test + void shouldFindDigitZeroInZero() { + assertThat(Numbers.containsDigit(0, 0)).isTrue(); + assertThat(Numbers.containsDigit(0, 1)).isFalse(); + } + + @Test + void shouldCountDigitsOfNegativeNumbers() { + assertThat(Numbers.length(-123)).isEqualTo(3); + assertThat(Numbers.length(-7)).isEqualTo(1); + assertThat(Numbers.length(Integer.MIN_VALUE)).isEqualTo(10); + } } From d59eba5d87b0197fa0e530c42ab5ef0ef148189a Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:19:50 +0100 Subject: [PATCH 05/10] Fix Statistics empty input, lcm zero/overflow and locale-dependent isAnagram (B18, B19, B22-B24) - Statistics.median and meanAbsoluteDeviation reject empty data with IllegalArgumentException (was ArrayIndexOutOfBoundsException / NaN) - NumberTheory.lcm returns 0 when either number is 0 (was division by zero) and throws ArithmeticException on overflow via Math.multiplyExact (lcm(4000000007, 4000000009) was negative) - StringUtils.isAnagram lower-cases with Locale.ROOT, so "TITLE" and "title" are anagrams under a Turkish default locale too Each case has a test that failed before the fix. Co-Authored-By: Claude Opus 5.5 --- .../diegourban/algorithms/math/NumberTheory.java | 11 +++++++---- .../diegourban/algorithms/math/Statistics.java | 13 +++++++++++-- .../diegourban/algorithms/string/StringUtils.java | 4 ++-- .../algorithms/math/NumberTheoryTest.java | 14 ++++++++++++++ .../diegourban/algorithms/math/StatisticsTest.java | 11 +++++++++++ .../algorithms/string/StringUtilsTest.java | 11 +++++++++++ 6 files changed, 56 insertions(+), 8 deletions(-) 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..a569e48 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)); } /** 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/string/StringUtils.java b/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java index 8db1e12..5b580f1 100644 --- a/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java +++ b/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java @@ -41,7 +41,7 @@ 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 + * 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} @@ -60,7 +60,7 @@ public static boolean isAnagram(final String a, final String b) { } private static char[] sort(final String original) { - final char[] chars = original.toLowerCase().toCharArray(); + final char[] chars = original.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/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 primeFactors = NumberTheory.primeFactorsSet(7); assertThat(primeFactors).hasSize(1).contains(7); } + + @Test + void shouldReturnZeroForTheLeastCommonMultipleWithZero() { + assertThat(NumberTheory.lcm(0, 0)).isZero(); + assertThat(NumberTheory.lcm(0, 5)).isZero(); + assertThat(NumberTheory.lcm(5, 0)).isZero(); + } + + @Test + void shouldFailInsteadOfOverflowingTheLeastCommonMultiple() { + assertThatExceptionOfType(ArithmeticException.class) + .isThrownBy(() -> NumberTheory.lcm(4_000_000_007L, 4_000_000_009L)); + } } diff --git a/src/test/java/io/github/diegourban/algorithms/math/StatisticsTest.java b/src/test/java/io/github/diegourban/algorithms/math/StatisticsTest.java index 69556e3..25da7c0 100644 --- a/src/test/java/io/github/diegourban/algorithms/math/StatisticsTest.java +++ b/src/test/java/io/github/diegourban/algorithms/math/StatisticsTest.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.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.within; import org.junit.jupiter.api.Test; @@ -47,4 +48,14 @@ void shouldCalculateAverageAbsoluteDeviation() { assertThat(Statistics.meanAbsoluteDeviation(new double[] {2, 2, 3, 4, 14})) .isCloseTo(3.6, within(0.1)); } + + @Test + void shouldRejectEmptyDataForMedianAndDeviation() { + assertThatIllegalArgumentException() + .isThrownBy(() -> Statistics.median(new double[] {})) + .withMessage("data must not be empty"); + assertThatIllegalArgumentException() + .isThrownBy(() -> Statistics.meanAbsoluteDeviation(new double[] {})) + .withMessage("data must not be empty"); + } } diff --git a/src/test/java/io/github/diegourban/algorithms/string/StringUtilsTest.java b/src/test/java/io/github/diegourban/algorithms/string/StringUtilsTest.java index 2f4ff4f..9a628bd 100644 --- a/src/test/java/io/github/diegourban/algorithms/string/StringUtilsTest.java +++ b/src/test/java/io/github/diegourban/algorithms/string/StringUtilsTest.java @@ -24,4 +24,15 @@ void shouldNotConsiderPalindrome() { assertThat(StringUtils.isPalindrome("ab")).isFalse(); assertThat(StringUtils.isPalindrome("abb")).isFalse(); } + + @Test + void shouldCompareAnagramsIndependentlyOfTheDefaultLocale() { + final java.util.Locale original = java.util.Locale.getDefault(); + java.util.Locale.setDefault(java.util.Locale.forLanguageTag("tr-TR")); + try { + assertThat(StringUtils.isAnagram("TITLE", "title")).isTrue(); + } finally { + java.util.Locale.setDefault(original); + } + } } From e6fcd6aac541da0f804c943557a44453ec904494 Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:20:43 +0100 Subject: [PATCH 06/10] Fix comments and messages (B26, B28, M6, N18, O1-O3, O7-O11) - B26: fibonacciSimpleBinetFormula is exact (rounded) up to n = 70, not 67; Javadoc and the exhaustive property range now say 70 - B28: BinarySearch.find uses O(log n) stack space, not O(1) - M6: explain the 6k +/- 1 step in isPrime - N18: TriangleType display names in English: Equilateral, Scalene, Isosceles (was Equilatero, Escaleno, Isoceles) - O1/O2: LinkedList exception messages in English, with the position - O3: Sequences.fibonacci message is "n must be non-negative: " - O7: drop commented-out code in Geometry.distance - O8: replace the formatter-split trailing comments in containsDigit - O9: drop comments that restate the code - O10: "gdc" typo; O11: name the Fisher-Yates shuffle Co-Authored-By: Claude Opus 5.5 --- .../algorithms/datastructure/array/ArrayUtils.java | 2 +- .../algorithms/datastructure/linkedlist/LinkedList.java | 8 ++++---- .../io/github/diegourban/algorithms/math/Geometry.java | 1 - .../github/diegourban/algorithms/math/NumberTheory.java | 7 ++++--- .../io/github/diegourban/algorithms/math/Numbers.java | 7 +++---- .../io/github/diegourban/algorithms/math/Sequences.java | 9 ++++----- .../github/diegourban/algorithms/math/TriangleType.java | 8 ++++---- .../diegourban/algorithms/search/BinarySearch.java | 2 +- .../github/diegourban/algorithms/string/StringUtils.java | 4 ++-- .../algorithms/math/SequencesPropertyTest.java | 2 +- .../github/diegourban/algorithms/math/SequencesTest.java | 2 +- .../diegourban/algorithms/math/TriangleTypeTest.java | 2 +- 12 files changed, 26 insertions(+), 28 deletions(-) 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..2428804 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 @@ -12,7 +12,7 @@ private ArrayUtils() {} private static final Random rand = 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. 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 12dc7af..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 @@ -87,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++) { @@ -123,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(); @@ -143,7 +143,7 @@ public void removeFromStart() { */ public void removeFromEnd() { if (this.totalElements == 0) { - throw new IllegalStateException("Lista vazia"); + throw new IllegalStateException("List is empty"); } if (this.totalElements == 1) { this.removeFromStart(); @@ -172,7 +172,7 @@ public void remove(final int position) { this.removeFromEnd(); } else { if (!positionOccupied(position)) { - throw new IllegalArgumentException("Posição inexistente"); + throw new IllegalArgumentException("Invalid position: " + position); } final LinkedListNode previous = this.getNode(position - 1); final LinkedListNode current = previous.getNext(); 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..25e122c 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); } 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 a569e48..bd85f64 100644 --- a/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java +++ b/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java @@ -42,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 @@ -59,14 +59,14 @@ public static long gcdLoop(final long a, final long b) { long bNumber = b; while (bNumber > 0) { final long temp = bNumber; - bNumber = gcd % bNumber; // % is remainder + bNumber = gcd % bNumber; gcd = temp; } return gcd; } /** - * 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 @@ -101,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 105eb04..05f901a 100644 --- a/src/main/java/io/github/diegourban/algorithms/math/Numbers.java +++ b/src/main/java/io/github/diegourban/algorithms/math/Numbers.java @@ -24,11 +24,10 @@ public static boolean containsDigit(final long number, final long digit) { return digit == 0; } + // take the last digit, then chop it off, until no digits are left 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 + thisDigit = thisNumber % 10; + thisNumber = thisNumber / 10; if (thisDigit == digit) { return true; } 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..f5bb22b 100644 --- a/src/main/java/io/github/diegourban/algorithms/math/Sequences.java +++ b/src/main/java/io/github/diegourban/algorithms/math/Sequences.java @@ -24,9 +24,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 +34,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 +52,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 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..4603711 100644 --- a/src/main/java/io/github/diegourban/algorithms/search/BinarySearch.java +++ b/src/main/java/io/github/diegourban/algorithms/search/BinarySearch.java @@ -21,7 +21,7 @@ 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 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 5b580f1..0132ed1 100644 --- a/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java +++ b/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java @@ -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, independently of the default locale) 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} diff --git a/src/test/java/io/github/diegourban/algorithms/math/SequencesPropertyTest.java b/src/test/java/io/github/diegourban/algorithms/math/SequencesPropertyTest.java index 9cb25d3..ce3cf8f 100644 --- a/src/test/java/io/github/diegourban/algorithms/math/SequencesPropertyTest.java +++ b/src/test/java/io/github/diegourban/algorithms/math/SequencesPropertyTest.java @@ -19,7 +19,7 @@ void fibonacciIsExactUpTo46(@ForAll @IntRange(min = 0, max = 46) final int n) { } @Property(generation = GenerationMode.EXHAUSTIVE) - void simpleBinetFormulaRoundsToTheExactValueUpTo67(@ForAll @IntRange(min = 0, max = 67) final int n) { + void simpleBinetFormulaRoundsToTheExactValueUpTo70(@ForAll @IntRange(min = 0, max = 70) final int n) { assertThat(BigInteger.valueOf(Math.round(Sequences.fibonacciSimpleBinetFormula(n)))) .isEqualTo(exactFibonacci(n)); } diff --git a/src/test/java/io/github/diegourban/algorithms/math/SequencesTest.java b/src/test/java/io/github/diegourban/algorithms/math/SequencesTest.java index b1d893a..62dcf76 100644 --- a/src/test/java/io/github/diegourban/algorithms/math/SequencesTest.java +++ b/src/test/java/io/github/diegourban/algorithms/math/SequencesTest.java @@ -51,6 +51,6 @@ void shouldCalculateFibonacciWithBinetFormula() { void shouldRejectNegativeFibonacciIndex() { assertThatIllegalArgumentException() .isThrownBy(() -> Sequences.fibonacci(-1)) - .withMessage("Index was negative. No such thing as a negative index in a series."); + .withMessage("n must be non-negative: -1"); } } diff --git a/src/test/java/io/github/diegourban/algorithms/math/TriangleTypeTest.java b/src/test/java/io/github/diegourban/algorithms/math/TriangleTypeTest.java index 078a567..ceb7649 100644 --- a/src/test/java/io/github/diegourban/algorithms/math/TriangleTypeTest.java +++ b/src/test/java/io/github/diegourban/algorithms/math/TriangleTypeTest.java @@ -8,7 +8,7 @@ class TriangleTypeTest { @ParameterizedTest - @CsvSource({"EQUILATERAL, Equilatero", "ISOSCELES, Isoceles", "ESCALENE, Escaleno"}) + @CsvSource({"EQUILATERAL, Equilateral", "ISOSCELES, Isosceles", "ESCALENE, Scalene"}) void exposesDisplayName(final TriangleType type, final String expectedName) { assertThat(type.getName()).isEqualTo(expectedName); } From 3f5c25226aa50f32580c1f4579544143c865cfbd Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:21:29 +0100 Subject: [PATCH 07/10] Name magic numbers (M1-M5, M7, M8) - M1: Set.BUCKET_COUNT (26) - M2: Vector.INITIAL_CAPACITY (100) - M3/M4: Measurement.KILOMETERS_PER_MILE and MILES_PER_KILOMETER - M5: Sequences.SQRT_5 and GOLDEN_RATIO instead of recomputing sqrt(5) four times; the exhaustive Fibonacci properties confirm identical results - M7: BigInteger.ZERO/ONE/TWO and BigInteger.valueOf instead of parsing strings - M8: Reader.BUFFER_SIZE is static Co-Authored-By: Claude Opus 5.5 --- .../algorithms/datastructure/set/Set.java | 6 ++++-- .../datastructure/vector/Vector.java | 4 +++- .../diegourban/algorithms/io/Reader.java | 2 +- .../algorithms/math/Combinatorics.java | 10 +++++----- .../algorithms/math/Measurement.java | 7 +++++-- .../diegourban/algorithms/math/Sequences.java | 20 +++++++++---------- 6 files changed, 28 insertions(+), 21 deletions(-) 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 712d795..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,11 +10,13 @@ */ public class Set { + private static final int BUCKET_COUNT = 26; + private final List> table = new LinkedList>(); /** 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()); } } @@ -56,7 +58,7 @@ private int calculateTableIndex(final String word) { if (word.isEmpty()) { throw new IllegalArgumentException("word must not be empty"); } - return word.toLowerCase().charAt(0) % 26; + return word.toLowerCase().charAt(0) % BUCKET_COUNT; } /** diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java b/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java index 60c6eec..850da21 100644 --- a/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java +++ b/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java @@ -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. */ 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..b6c31b1 100644 --- a/src/main/java/io/github/diegourban/algorithms/io/Reader.java +++ b/src/main/java/io/github/diegourban/algorithms/io/Reader.java @@ -20,7 +20,7 @@ */ public class Reader { - private final int BUFFER_SIZE = 1 << 16; + private static final int BUFFER_SIZE = 1 << 16; private DataInputStream din; private final byte[] buffer; private int bufferPointer, bytesRead; 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/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/Sequences.java b/src/main/java/io/github/diegourban/algorithms/math/Sequences.java index f5bb22b..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() {} /** @@ -65,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); } /** @@ -83,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(); } } From 88c71399fe5a667fbf3c4b131227d75002984832 Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:22:59 +0100 Subject: [PATCH 08/10] Rename unclear private names, parameters and locals (N1-N11, N14, N20-N25, N27) Only non-API names: private members, parameters and locals, e.g. theArray -> values, arrA -> sortedA, bNumber -> divisor, minorsFound -> boundary, analysis -> position, guaranteesSpace -> ensureCapacity, Reader's din/buf/cnt/c/ret/neg -> input/line/length/ nextByte/result/negative. Verified: compiled without debug info, the bytecode is identical to before except for the six renamed private members (RANDOM, randomBetweenInclusive, sortedLowerCaseChars, ensureCapacity, findSmallestIndex, input). Co-Authored-By: Claude Opus 5.5 --- .../datastructure/array/ArrayUtils.java | 24 ++-- .../datastructure/vector/Vector.java | 6 +- .../diegourban/algorithms/io/Reader.java | 116 +++++++++--------- .../diegourban/algorithms/math/Geometry.java | 46 +++---- .../algorithms/math/NumberTheory.java | 14 +-- .../diegourban/algorithms/math/Numbers.java | 34 ++--- .../algorithms/search/BinarySearch.java | 12 +- .../algorithms/sorting/InsertionSort.java | 14 +-- .../algorithms/sorting/MergeSort.java | 24 ++-- .../algorithms/sorting/QuickSort.java | 10 +- .../algorithms/sorting/SelectionSort.java | 18 +-- .../algorithms/string/StringUtils.java | 26 ++-- 12 files changed, 172 insertions(+), 172 deletions(-) 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 2428804..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,7 +9,7 @@ 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 (Fisher–Yates) shuffle of an array. @@ -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/vector/Vector.java b/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java index 850da21..1a94dc5 100644 --- a/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java +++ b/src/main/java/io/github/diegourban/algorithms/datastructure/vector/Vector.java @@ -23,7 +23,7 @@ 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++; @@ -42,7 +42,7 @@ public void add(final int position, final Element element) { 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]; } @@ -115,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 b6c31b1..af6f308 100644 --- a/src/main/java/io/github/diegourban/algorithms/io/Reader.java +++ b/src/main/java/io/github/diegourban/algorithms/io/Reader.java @@ -21,7 +21,7 @@ public class Reader { private static final int BUFFER_SIZE = 1 << 16; - private DataInputStream din; + 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/Geometry.java b/src/main/java/io/github/diegourban/algorithms/math/Geometry.java index 25e122c..01ea5ad 100644 --- a/src/main/java/io/github/diegourban/algorithms/math/Geometry.java +++ b/src/main/java/io/github/diegourban/algorithms/math/Geometry.java @@ -114,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; } @@ -148,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/NumberTheory.java b/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java index bd85f64..39e8348 100644 --- a/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java +++ b/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java @@ -55,14 +55,14 @@ 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; - gcd = temp; + long dividend = a; + long divisor = b; + while (divisor > 0) { + final long temp = divisor; + divisor = dividend % divisor; + dividend = temp; } - return gcd; + return dividend; } /** 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 05f901a..36749be 100644 --- a/src/main/java/io/github/diegourban/algorithms/math/Numbers.java +++ b/src/main/java/io/github/diegourban/algorithms/math/Numbers.java @@ -17,18 +17,18 @@ private Numbers() {} * @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; - if (thisNumber == 0) { + if (remaining == 0) { return digit == 0; } // take the last digit, then chop it off, until no digits are left - while (thisNumber != 0) { - thisDigit = thisNumber % 10; - thisNumber = thisNumber / 10; - if (thisDigit == digit) { + while (remaining != 0) { + lastDigit = remaining % 10; + remaining = remaining / 10; + if (lastDigit == digit) { return true; } } @@ -48,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; } } 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 4603711..e95e64b 100644 --- a/src/main/java/io/github/diegourban/algorithms/search/BinarySearch.java +++ b/src/main/java/io/github/diegourban/algorithms/search/BinarySearch.java @@ -26,10 +26,10 @@ public static int indexOf(final int[] sorted, final int key) { * @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 0132ed1..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; } } @@ -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(Locale.ROOT).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; } From ecf1ebfba4865949c789c20e0868f113ca10059c Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:23:17 +0100 Subject: [PATCH 09/10] Ignore the local findings.md review list Co-Authored-By: Claude Opus 5.5 --- .gitignore | 1 + 1 file changed, 1 insertion(+) 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 From 2ce7cd6565540737f82c2e815afd20ba6fd252f6 Mon Sep 17 00:00:00 2001 From: Diego Leonardo Urban Date: Fri, 6 Nov 2026 17:24:59 +0100 Subject: [PATCH 10/10] Test Reader.readLine with carriage-return line endings Covers the '\r' branch of the line-ending check, which no test reached. Co-Authored-By: Claude Opus 5.5 --- .../io/github/diegourban/algorithms/io/ReaderTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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"); + } }