6.1.5
2.4.17
diff --git a/algorithms-modules/algorithms-genetic/README.md b/algorithms-modules/algorithms-genetic/README.md
deleted file mode 100644
index eb4e3fb798e8..000000000000
--- a/algorithms-modules/algorithms-genetic/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-## Genetic Algorithms
-
-This module contains articles about genetic algorithms.
-
-### Relevant articles:
-
-- [Introduction to Jenetics Library](https://www.baeldung.com/jenetics)
-- [Ant Colony Optimization with a Java Example](https://www.baeldung.com/java-ant-colony-optimization)
-- [Design a Genetic Algorithm in Java](https://www.baeldung.com/java-genetic-algorithm)
-- [The Traveling Salesman Problem in Java](https://www.baeldung.com/java-simulated-annealing-for-traveling-salesman)
diff --git a/algorithms-modules/algorithms-miscellaneous-1/README.md b/algorithms-modules/algorithms-miscellaneous-1/README.md
deleted file mode 100644
index a3b6cbdaf831..000000000000
--- a/algorithms-modules/algorithms-miscellaneous-1/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## Algorithms - Miscellaneous
-
-This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/../algorithms-sorting) and
-[genetic algorithms](/../algorithms-genetic), have their own dedicated modules.
-
-### Relevant articles:
-
-- [Dijkstra Shortest Path Algorithm in Java](https://www.baeldung.com/java-dijkstra)
-- [Implementing Simple State Machines with Java Enums](https://www.baeldung.com/java-enum-simple-state-machine)
-- [Permutations of an Array in Java](https://www.baeldung.com/java-array-permutations)
-- [Maximum Subarray Problem in Java](https://www.baeldung.com/java-maximum-subarray)
-- [Converting Between Byte Arrays and Hexadecimal Strings in Java](https://www.baeldung.com/java-byte-arrays-hex-strings)
-- [Calculate Distance Between Two Coordinates in Java](https://www.baeldung.com/java-find-distance-between-points)
-- More articles: [[next -->]](/algorithms-miscellaneous-2)
diff --git a/algorithms-modules/algorithms-miscellaneous-1/pom.xml b/algorithms-modules/algorithms-miscellaneous-1/pom.xml
index 38ea383c32cd..bcadae04efc9 100644
--- a/algorithms-modules/algorithms-miscellaneous-1/pom.xml
+++ b/algorithms-modules/algorithms-miscellaneous-1/pom.xml
@@ -68,7 +68,6 @@
- 3.3.0
4.0.0
diff --git a/algorithms-modules/algorithms-miscellaneous-10/pom.xml b/algorithms-modules/algorithms-miscellaneous-10/pom.xml
index 12c475f4a86d..0fa21df800b9 100644
--- a/algorithms-modules/algorithms-miscellaneous-10/pom.xml
+++ b/algorithms-modules/algorithms-miscellaneous-10/pom.xml
@@ -13,4 +13,4 @@
1.0.0-SNAPSHOT
-
\ No newline at end of file
+
diff --git a/algorithms-modules/algorithms-miscellaneous-10/src/main/java/com/baeldung/algorithms/fizzbuzz/FizzBuzz.java b/algorithms-modules/algorithms-miscellaneous-10/src/main/java/com/baeldung/algorithms/fizzbuzz/FizzBuzz.java
new file mode 100644
index 000000000000..4e3a8feb8936
--- /dev/null
+++ b/algorithms-modules/algorithms-miscellaneous-10/src/main/java/com/baeldung/algorithms/fizzbuzz/FizzBuzz.java
@@ -0,0 +1,93 @@
+package com.baeldung.algorithms.fizzbuzz;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * FizzBuzz implementation that demonstrates three different approaches to solving
+ * the classic FizzBuzz programming puzzle.
+ *
+ * Problem Stmt: Given a positive integer n, iterate over 1 to n, print "Fizz" for multiples of 3, "Buzz" for
+ * multiples of 5, "FizzBuzz" for multiples of both, and the number otherwise.
+ */
+public class FizzBuzz {
+
+ /**
+ * Naive approach using explicit modulo checks with an if-else chain.
+ * Order of conditions is critical here, so we must check divisibility
+ * by both 3 and 5 first.
+ *
+ * @param n positive integer (we iterate from 1 to n inclusive)
+ * @return FizzBuzz list with n elements
+ */
+ public List fizzBuzzNaive(int n) {
+ List result = new ArrayList<>();
+ for (int i = 1; i <= n; i++) {
+ if (i % 3 == 0 && i % 5 == 0) {
+ result.add("FizzBuzz");
+ } else if (i % 3 == 0) {
+ result.add("Fizz");
+ } else if (i % 5 == 0) {
+ result.add("Buzz");
+ } else {
+ result.add(String.valueOf(i));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * String concatenation approach that elegantly handles the FizzBuzz case.
+ * It uses StringBuilder and reuses the same object by setting its length to 0
+ * using setLength(0) to avoid repeated instantiation.
+ *
+ * @param n positive integer (we iterate from 1 to n inclusive)
+ * @return FizzBuzz list with n elements
+ * @see Clearing StringBuilder
+ */
+ public List fizzBuzzConcatenation(int n) {
+ List result = new ArrayList<>();
+ StringBuilder output = new StringBuilder();
+ for (int i = 1; i <= n; i++) {
+ if (i % 3 == 0) {
+ output.append("Fizz");
+ }
+ if (i % 5 == 0) {
+ output.append("Buzz");
+ }
+ result.add(output.length() > 0 ? output.toString() : String.valueOf(i));
+ output.setLength(0);
+ }
+ return result;
+ }
+
+ /**
+ * Counter approach that eliminates modulo operations using counters.
+ * It also uses StringBuilder and reuses the same object by settings
+ * its length to 0 using setLength(0) to avoid repeated instantiation.
+ *
+ * @param n positive integer (we iterate from 1 to n inclusive)
+ * @return FizzBuzz list with n elements
+ */
+ public List fizzBuzzCounter(int n) {
+ List result = new ArrayList<>();
+ StringBuilder output = new StringBuilder();
+ int fizz = 0;
+ int buzz = 0;
+ for (int i = 1; i <= n; i++) {
+ fizz++;
+ buzz++;
+ if (fizz == 3) {
+ output.append("Fizz");
+ fizz = 0;
+ }
+ if (buzz == 5) {
+ output.append("Buzz");
+ buzz = 0;
+ }
+ result.add(output.length() > 0 ? output.toString() : String.valueOf(i));
+ output.setLength(0);
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-miscellaneous-10/src/main/java/com/baeldung/algorithms/randomuniqueidentifier/UniqueIdGenerator.java b/algorithms-modules/algorithms-miscellaneous-10/src/main/java/com/baeldung/algorithms/randomuniqueidentifier/UniqueIdGenerator.java
new file mode 100644
index 000000000000..e6c625103347
--- /dev/null
+++ b/algorithms-modules/algorithms-miscellaneous-10/src/main/java/com/baeldung/algorithms/randomuniqueidentifier/UniqueIdGenerator.java
@@ -0,0 +1,48 @@
+package com.baeldung.algorithms.randomuniqueidentifier;
+
+import java.security.SecureRandom;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Custom Random Identifier generator with unique ids .
+ */
+public class UniqueIdGenerator {
+
+ private static final String ALPHANUMERIC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ private static final SecureRandom random = new SecureRandom();
+
+ private int idLength = 8; // Default length
+
+ /**
+ * Overrides the default ID length for generated identifiers.
+ * @param idLength The desired length (must be positive).
+ */
+ public void setIdLength(int idLength) {
+ if (idLength <= 0) {
+ throw new IllegalArgumentException("Length must be positive");
+ }
+ this.idLength = idLength;
+ }
+
+ /**
+ * Generates a unique alphanumeric ID using the SecureRandom character mapping approach.
+ * @param existingIds A set of IDs already in use to ensure uniqueness.
+ * @return A unique alphanumeric string.
+ */
+ public String generateUniqueId(Set existingIds) {
+ String newId;
+ do {
+ newId = generateRandomString(this.idLength);
+ } while (existingIds.contains(newId));
+ return newId;
+ }
+
+ private String generateRandomString(int length) {
+ return random.ints(length, 0, ALPHANUMERIC.length())
+ .mapToObj(ALPHANUMERIC::charAt)
+ .map(Object::toString)
+ .collect(Collectors.joining());
+ }
+}
+
diff --git a/algorithms-modules/algorithms-miscellaneous-10/src/test/java/com/baeldung/algorithms/fizzbuzz/FizzBuzzUnitTest.java b/algorithms-modules/algorithms-miscellaneous-10/src/test/java/com/baeldung/algorithms/fizzbuzz/FizzBuzzUnitTest.java
new file mode 100644
index 000000000000..1dbaf745f902
--- /dev/null
+++ b/algorithms-modules/algorithms-miscellaneous-10/src/test/java/com/baeldung/algorithms/fizzbuzz/FizzBuzzUnitTest.java
@@ -0,0 +1,73 @@
+package com.baeldung.algorithms.fizzbuzz;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * JUnit 5 test suite for FizzBuzz implementation.
+ * It tests all three approaches: Naive, Concatenation, and Counter.
+ *
+ * Test naming convention: givenWW_whenYY_thenXX
+ */
+class FizzBuzzUnitTest {
+
+ private FizzBuzz fizzBuzz;
+
+ private static final List GROUND_TRUTH_SEQUENCE_LENGTH_5 = generateGroundTruth(5);
+
+ private static final List GROUND_TRUTH_SEQUENCE_LENGTH_100 = generateGroundTruth(100);
+
+ @BeforeEach
+ void setUp() {
+ fizzBuzz = new FizzBuzz();
+ }
+
+ @Test
+ void givenSequenceLength5_whenAllMethods_thenReturnCorrectSequence() {
+ List naiveResult = fizzBuzz.fizzBuzzNaive(5);
+ List concatResult = fizzBuzz.fizzBuzzConcatenation(5);
+ List counterResult = fizzBuzz.fizzBuzzCounter(5);
+
+ assertAll(
+ () -> assertEquals(GROUND_TRUTH_SEQUENCE_LENGTH_5, naiveResult,
+ "fizzBuzzNaive should return correct sequence for n=5"),
+ () -> assertEquals(GROUND_TRUTH_SEQUENCE_LENGTH_5, concatResult,
+ "fizzBuzzConcatenation should return correct sequence for n=5"),
+ () -> assertEquals(GROUND_TRUTH_SEQUENCE_LENGTH_5, counterResult,
+ "fizzBuzzCounter should return correct sequence for n=5")
+ );
+ }
+
+ @Test
+ void givenSequenceLength100_whenAllMethods_thenReturnCorrectSequence() {
+ List naiveResult = fizzBuzz.fizzBuzzNaive(100);
+ List concatResult = fizzBuzz.fizzBuzzConcatenation(100);
+ List counterResult = fizzBuzz.fizzBuzzCounter(100);
+
+ assertAll(
+ () -> assertEquals(GROUND_TRUTH_SEQUENCE_LENGTH_100, naiveResult,
+ "fizzBuzzNaive should return correct sequence for n=100"),
+ () -> assertEquals(GROUND_TRUTH_SEQUENCE_LENGTH_100, concatResult,
+ "fizzBuzzConcatenation should return correct sequence for n=100"),
+ () -> assertEquals(GROUND_TRUTH_SEQUENCE_LENGTH_100, counterResult,
+ "fizzBuzzCounter should return correct sequence for n=100")
+ );
+ }
+
+ private static List generateGroundTruth(int n) {
+ return IntStream.rangeClosed(1, n)
+ .mapToObj(i -> {
+ if (i % 15 == 0) return "FizzBuzz";
+ if (i % 3 == 0) return "Fizz";
+ if (i % 5 == 0) return "Buzz";
+ return String.valueOf(i);
+ })
+ .collect(Collectors.toList());
+ }
+}
diff --git a/algorithms-modules/algorithms-miscellaneous-10/src/test/java/com/baeldung/algorithms/randomuniqueidentifier/UniqueIdGeneratorUnitTest.java b/algorithms-modules/algorithms-miscellaneous-10/src/test/java/com/baeldung/algorithms/randomuniqueidentifier/UniqueIdGeneratorUnitTest.java
new file mode 100644
index 000000000000..8413210be38e
--- /dev/null
+++ b/algorithms-modules/algorithms-miscellaneous-10/src/test/java/com/baeldung/algorithms/randomuniqueidentifier/UniqueIdGeneratorUnitTest.java
@@ -0,0 +1,75 @@
+package com.baeldung.algorithms.randomuniqueidentifier;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class UniqueIdGeneratorUnitTest {
+
+ private UniqueIdGenerator generator;
+ private static final Pattern ALPHANUMERIC_PATTERN = Pattern.compile("^[a-zA-Z0-9]+$");
+
+ @BeforeEach
+ void setUp() {
+ generator = new UniqueIdGenerator();
+ }
+
+ @Test
+ void givenDefaultSettings_whenGenerateUniqueIdIsCalled_thenReturnsValidAlphanumericString() {
+ Set existingIds = new HashSet<>();
+
+ String id = generator.generateUniqueId(existingIds);
+
+ assertNotNull(id);
+ assertEquals(8, id.length());
+ assertTrue(ALPHANUMERIC_PATTERN.matcher(id).matches());
+ }
+
+ @Test
+ void givenCustomLength_whenSetIdLengthIsCalled_thenGeneratorRespectsNewLength() {
+ generator.setIdLength(5);
+ Set existingIds = new HashSet<>();
+
+ String id = generator.generateUniqueId(existingIds);
+
+ assertEquals(5, id.length());
+ }
+
+ @Test
+ void givenExistingId_whenGenerateUniqueIdIsCalled_thenReturnsNonCollidingId() {
+ // GIVEN: A set that already contains a specific ID
+ Set existingIds = new HashSet<>();
+ String existingId = "ABC12345";
+ existingIds.add(existingId);
+
+ // WHEN: We generate a new ID
+ String newId = generator.generateUniqueId(existingIds);
+
+ // THEN: The new ID must not match the existing one
+ assertNotEquals(existingId, newId);
+ }
+
+ @Test
+ void givenLargeNumberRequests_whenGeneratedInBulk_thenAllIdsAreUnique() {
+ Set store = new HashSet<>();
+ int count = 100;
+
+ for (int i = 0; i < count; i++) {
+ store.add(generator.generateUniqueId(store));
+ }
+
+ assertEquals(count, store.size(), "All 100 generated IDs should be unique");
+ }
+
+ @Test
+ void givenInvalidLength_whenSetIdLengthIsCalled_thenThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ generator.setIdLength(0);
+ });
+ }
+}
+
diff --git a/algorithms-modules/algorithms-miscellaneous-2/README.md b/algorithms-modules/algorithms-miscellaneous-2/README.md
deleted file mode 100644
index a51d786ae06f..000000000000
--- a/algorithms-modules/algorithms-miscellaneous-2/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Algorithms - Miscellaneous
-
-This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/../algorithms-sorting) and
-[genetic algorithms](/../algorithms-genetic), have their own dedicated modules.
-
-### Relevant articles:
-
-- [Introduction to Cobertura](https://www.baeldung.com/cobertura)
-- [Test a Linked List for Cyclicity](https://www.baeldung.com/java-linked-list-cyclicity)
-- [Introduction to JGraphT](https://www.baeldung.com/jgrapht)
-- [A Maze Solver in Java](https://www.baeldung.com/java-solve-maze)
-- [Create a Sudoku Solver in Java](https://www.baeldung.com/java-sudoku)
-- [Displaying Money Amounts in Words](https://www.baeldung.com/java-money-into-words)
-- [A Collaborative Filtering Recommendation System in Java](https://www.baeldung.com/java-collaborative-filtering-recommendations)
-- [Implementing A* Pathfinding in Java](https://www.baeldung.com/java-a-star-pathfinding)
-- More articles: [[<-- prev]](/algorithms-miscellaneous-1) [[next -->]](/algorithms-miscellaneous-3)
diff --git a/algorithms-modules/algorithms-miscellaneous-3/README.md b/algorithms-modules/algorithms-miscellaneous-3/README.md
deleted file mode 100644
index 470ab0485291..000000000000
--- a/algorithms-modules/algorithms-miscellaneous-3/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Algorithms - Miscellaneous
-
-This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/algorithms-sorting) and
-[genetic algorithms](/algorithms-genetic), have their own dedicated modules.
-
-## Relevant Articles:
-
-- [Java Two Pointer Technique](https://www.baeldung.com/java-two-pointer-technique)
-- [Converting Between Roman and Arabic Numerals in Java](https://www.baeldung.com/java-convert-roman-arabic)
-- [Checking If a List Is Sorted in Java](https://www.baeldung.com/java-check-if-list-sorted)
-- [Checking if a Java Graph Has a Cycle](https://www.baeldung.com/java-graph-has-a-cycle)
-- [A Guide to the Folding Technique in Java](https://www.baeldung.com/folding-hashing-technique)
-- [Creating a Triangle with for Loops in Java](https://www.baeldung.com/java-print-triangle)
-- [The K-Means Clustering Algorithm in Java](https://www.baeldung.com/java-k-means-clustering-algorithm)
-- [Validating Input with Finite Automata in Java](https://www.baeldung.com/java-finite-automata)
-- More articles: [[<-- prev]](/algorithms-miscellaneous-2) [[next -->]](/algorithms-miscellaneous-4)
diff --git a/algorithms-modules/algorithms-miscellaneous-4/README.md b/algorithms-modules/algorithms-miscellaneous-4/README.md
deleted file mode 100644
index 9b5b2c74018e..000000000000
--- a/algorithms-modules/algorithms-miscellaneous-4/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-## Algorithms - Miscellaneous
-
-This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](https://github.com/eugenp/tutorials/blob/algorithms-sorting) and [genetic algorithms](https://github.com/eugenp/tutorials/blob/algorithms-genetic), have their own dedicated modules.
-
-### Relevant articles:
-
-- [Multi-Swarm Optimization Algorithm in Java](https://www.baeldung.com/java-multi-swarm-algorithm)
-- [Check if a String Contains All the Letters of the Alphabet With Java](https://www.baeldung.com/java-string-contains-all-letters)
-- [Find the Middle Element of a Linked List in Java](https://www.baeldung.com/java-linked-list-middle-element)
-- [Find Substrings That Are Palindromes in Java](https://www.baeldung.com/java-palindrome-substrings)
-- [Find the Longest Substring Without Repeating Characters](https://www.baeldung.com/java-longest-substring-without-repeated-characters)
-- [Find the Smallest Missing Integer in an Array](https://www.baeldung.com/java-smallest-missing-integer-in-array)
-- [Permutations of a String in Java](https://www.baeldung.com/java-string-permutations)
-- [Example of Hill Climbing Algorithm in Java](https://www.baeldung.com/java-hill-climbing-algorithm)
-- More articles: [[<-- prev]](/algorithms-miscellaneous-3) [[next -->]](/algorithms-miscellaneous-5)
diff --git a/algorithms-modules/algorithms-miscellaneous-4/pom.xml b/algorithms-modules/algorithms-miscellaneous-4/pom.xml
index 7ecf0ada089f..5a45478a25d5 100644
--- a/algorithms-modules/algorithms-miscellaneous-4/pom.xml
+++ b/algorithms-modules/algorithms-miscellaneous-4/pom.xml
@@ -37,8 +37,4 @@
-
- 3.3.3
-
-
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-miscellaneous-5/README.md b/algorithms-modules/algorithms-miscellaneous-5/README.md
deleted file mode 100644
index bb49680e4450..000000000000
--- a/algorithms-modules/algorithms-miscellaneous-5/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## Algorithms - Miscellaneous
-
-This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/../algorithms-sorting) and
-[genetic algorithms](/../algorithms-genetic), have their own dedicated modules.
-
-### Relevant articles:
-
-- [Reversing a Binary Tree in Java](https://www.baeldung.com/java-reversing-a-binary-tree)
-- [Find If Two Numbers Are Relatively Prime in Java](https://www.baeldung.com/java-two-relatively-prime-numbers)
-- [Knapsack Problem Implementation in Java](https://www.baeldung.com/java-knapsack)
-- [How to Determine if a Binary Tree Is Balanced in Java](https://www.baeldung.com/java-balanced-binary-tree)
-- [Overview of Combinatorial Problems in Java](https://www.baeldung.com/java-combinatorial-algorithms)
-- [Prim’s Algorithm with a Java Implementation](https://www.baeldung.com/java-prim-algorithm)
-- [How to Merge Two Sorted Arrays in Java](https://www.baeldung.com/java-merge-sorted-arrays)
-- [Median of Stream of Integers using Heap in Java](https://www.baeldung.com/java-stream-integers-median-using-heap)
-- More articles: [[<-- prev]](/algorithms-miscellaneous-4) [[next -->]](/algorithms-miscellaneous-6)
-
diff --git a/algorithms-modules/algorithms-miscellaneous-5/pom.xml b/algorithms-modules/algorithms-miscellaneous-5/pom.xml
index 377e1d88a4cd..8c785ad0371e 100644
--- a/algorithms-modules/algorithms-miscellaneous-5/pom.xml
+++ b/algorithms-modules/algorithms-miscellaneous-5/pom.xml
@@ -7,7 +7,6 @@
0.0.1-SNAPSHOT
algorithms-miscellaneous-5
-
com.baeldung
algorithms-modules
@@ -47,13 +46,10 @@
17
17
+ 17
-
- 4.0.0
-
-
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-miscellaneous-6/README.md b/algorithms-modules/algorithms-miscellaneous-6/README.md
deleted file mode 100644
index f21eddeed895..000000000000
--- a/algorithms-modules/algorithms-miscellaneous-6/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-### Relevant Articles:
-
-- [Boruvka’s Algorithm for Minimum Spanning Trees in Java](https://www.baeldung.com/java-boruvka-algorithm)
-- [Gradient Descent in Java](https://www.baeldung.com/java-gradient-descent)
-- [Kruskal’s Algorithm for Spanning Trees with a Java Implementation](https://www.baeldung.com/java-spanning-trees-kruskal)
-- [Balanced Brackets Algorithm in Java](https://www.baeldung.com/java-balanced-brackets-algorithm)
-- [Efficiently Merge Sorted Java Sequences](https://www.baeldung.com/java-merge-sorted-sequences)
-- [Introduction to Greedy Algorithms with Java](https://www.baeldung.com/java-greedy-algorithms)
-- [The Caesar Cipher in Java](https://www.baeldung.com/java-caesar-cipher)
-- [Implementing a 2048 Solver in Java](https://www.baeldung.com/2048-java-solver)
-- [Finding Top K Elements in a Java Array](https://www.baeldung.com/java-array-top-elements)
-- [Reversing a Linked List in Java](https://www.baeldung.com/java-reverse-linked-list)
-- More articles: [[<-- prev]](/algorithms-miscellaneous-5)
diff --git a/algorithms-modules/algorithms-miscellaneous-7/README.md b/algorithms-modules/algorithms-miscellaneous-7/README.md
deleted file mode 100644
index 5de050525df7..000000000000
--- a/algorithms-modules/algorithms-miscellaneous-7/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-### Relevant Articles:
-
-- [Algorithm to Identify and Validate a Credit Card Number](https://www.baeldung.com/java-validate-cc-number)
-- [Find the N Most Frequent Elements in a Java Array](https://www.baeldung.com/java-n-most-frequent-elements-array)
-- [Getting Pixel Array From Image in Java](https://www.baeldung.com/java-getting-pixel-array-from-image)
-- [Rotate Arrays in Java](https://www.baeldung.com/java-rotate-arrays)
-- [Find Missing Number From a Given Array in Java](https://www.baeldung.com/java-array-find-missing-number)
-- [Calculate Weighted Mean in Java](https://www.baeldung.com/java-compute-weighted-average)
-- [Check if Two Strings Are Rotations of Each Other](https://www.baeldung.com/java-string-check-strings-rotations)
-- [Find the Largest Prime Under the Given Number in Java](https://www.baeldung.com/java-largest-prime-lower-threshold)
-- [Count the Number of Unique Digits in an Integer using Java](https://www.baeldung.com/java-int-count-unique-digits)
-- More articles: [[<-- prev]](/algorithms-miscellaneous-6)
diff --git a/algorithms-modules/algorithms-miscellaneous-8/README.md b/algorithms-modules/algorithms-miscellaneous-8/README.md
deleted file mode 100644
index 6c2af41f8f18..000000000000
--- a/algorithms-modules/algorithms-miscellaneous-8/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-### Relevant Articles:
-- [Vigenère Cipher in Java](https://www.baeldung.com/java-vigenere-cipher)
-- [Merge Overlapping Intervals in a Java Collection](https://www.baeldung.com/java-collection-merge-overlapping-intervals)
-- [Generate Juggler Sequence in Java](https://www.baeldung.com/java-generate-juggler-sequence)
-- [Finding the Parent of a Node in a Binary Search Tree with Java](https://www.baeldung.com/java-find-parent-node-binary-search-tree)
-- [Check if a Number Is a Happy Number in Java](https://www.baeldung.com/java-happy-sad-number-test)
-- [Find the Largest Number Possible After Removing k Digits of a Number](https://www.baeldung.com/java-find-largest-number-remove-k-digits)
-- [Implement Connect 4 Game with Java](https://www.baeldung.com/java-connect-4-game)
-- [SkipList Implementation in Java](https://www.baeldung.com/java-skiplist)
-- [Find the Date of Easter Sunday for the Given Year](https://www.baeldung.com/java-determine-easter-date-specific-year)
-- [Calculating Moving Averages in Java](https://www.baeldung.com/java-compute-moving-average)
-- More articles: [[<-- prev]](/algorithms-miscellaneous-7)
diff --git a/algorithms-modules/algorithms-miscellaneous-8/pom.xml b/algorithms-modules/algorithms-miscellaneous-8/pom.xml
index 086088114e92..67540f9fbd0d 100644
--- a/algorithms-modules/algorithms-miscellaneous-8/pom.xml
+++ b/algorithms-modules/algorithms-miscellaneous-8/pom.xml
@@ -20,4 +20,5 @@
${commons-math3.version}
+
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-miscellaneous-9/README.md b/algorithms-modules/algorithms-miscellaneous-9/README.md
deleted file mode 100644
index ec4c59de7766..000000000000
--- a/algorithms-modules/algorithms-miscellaneous-9/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-### Relevant Articles:
-- [Check if Two Strings Are Permutations of Each Other in Java](https://www.baeldung.com/java-check-permutations-two-strings)
-- [Finding the Mode of Integers in an Array in Java](https://www.baeldung.com/java-mode-integer-array)
-- [Counting an Occurrence in an Array](https://www.baeldung.com/java-array-count-distinct-elements-frequencies)
-- [Counting Subrrays Having a Specific Arithmetic Mean in Java](https://www.baeldung.com/java-count-subrrays-specified-average-value)
-- [Finding the Closest Number to a Given Value From a List of Integers in Java](https://www.baeldung.com/java-list-find-closest-integer)
-- [How to Find the Kth Largest Element in Java](https://www.baeldung.com/java-kth-largest-element)
-- [Introduction to Minimax Algorithm with a Java Implementation](https://www.baeldung.com/java-minimax-algorithm)
-- [How to Calculate Levenshtein Distance in Java?](https://www.baeldung.com/java-levenshtein-distance)
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-numeric/README.md b/algorithms-modules/algorithms-numeric/README.md
deleted file mode 100644
index 89228f87cd5a..000000000000
--- a/algorithms-modules/algorithms-numeric/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-### Relevant Articles:
-
-- [How to Check Number Perfection](https://www.baeldung.com/java-number-perfection-test)
-- [How to Check if a Number Is a Palindrome in Java](https://www.baeldung.com/java-palindrome-integer-test)
diff --git a/algorithms-modules/algorithms-numeric/pom.xml b/algorithms-modules/algorithms-numeric/pom.xml
index 4adcd2f2b398..968ce788a1b6 100644
--- a/algorithms-modules/algorithms-numeric/pom.xml
+++ b/algorithms-modules/algorithms-numeric/pom.xml
@@ -13,22 +13,33 @@
1.0.0-SNAPSHOT
-
- 1.35
-
-
org.openjdk.jmh
jmh-core
${jmh.version}
-
org.openjdk.jmh
jmh-generator-annprocess
${jmh.version}
+
+ org.nd4j
+ nd4j-api
+ ${nd4j.version}
+
+
+ org.nd4j
+ nd4j-native
+ ${nd4j.version}
+ runtime
+
+
+ 1.35
+ 1.0.0-M2.1
+
+
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/bcdtodecimal/BCDtoDecimalConverter.java b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/bcdtodecimal/BCDtoDecimalConverter.java
new file mode 100644
index 000000000000..1549f79f9e50
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/bcdtodecimal/BCDtoDecimalConverter.java
@@ -0,0 +1,48 @@
+package com.baeldung.algorithms.bcdtodecimal;
+
+public class BCDtoDecimalConverter {
+ /**
+ * Converts a single packed BCD byte to an integer.
+ * Each byte represents two decimal digits.
+ *
+ * @param bcdByte The BCD byte to convert.
+ * @return The decimal integer value.
+ * @throws IllegalArgumentException if any nibble contains a non-BCD value (>9).
+ */
+ public static int convertPackedByte(byte bcdByte) {
+ int resultDecimal;
+ int upperNibble = (bcdByte >> 4) & 0x0F;
+ int lowerNibble = bcdByte & 0x0F;
+ if (upperNibble > 9 || lowerNibble > 9) {
+ throw new IllegalArgumentException(
+ String.format("Invalid BCD format: byte 0x%02X contains non-decimal digit.", bcdByte)
+ );
+ }
+ resultDecimal = upperNibble * 10 + lowerNibble;
+ return resultDecimal;
+ }
+
+ /**
+ * Converts a BCD byte array to a long decimal value.
+ * Each byte in the array iis mapped to a packed BCD byte,
+ * representing two BCD nibbles.
+ *
+ * @param bcdArray The array of BCD bytes.
+ * @return The combined long decimal value.
+ * @throws IllegalArgumentException if any nibble contains a non-BCD value (>9).
+ */
+ public static long convertPackedByteArray(byte[] bcdArray) {
+ long resultDecimal = 0;
+ for (byte bcd : bcdArray) {
+ int upperNibble = (bcd >> 4) & 0x0F;
+ int lowerNibble = bcd & 0x0F;
+
+ if (upperNibble > 9 || lowerNibble > 9) {
+ throw new IllegalArgumentException("Invalid BCD format: nibble contains non-decimal digit.");
+ }
+
+ resultDecimal = resultDecimal * 100 + (upperNibble * 10 + lowerNibble);
+ }
+ return resultDecimal;
+ }
+}
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/fastgaussianblur/FastGaussianBlur.java b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/fastgaussianblur/FastGaussianBlur.java
new file mode 100644
index 000000000000..42b2ef10ba2c
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/fastgaussianblur/FastGaussianBlur.java
@@ -0,0 +1,100 @@
+package com.baeldung.algorithms.fastgaussianblur;
+
+/**
+ * Fast Gaussian Blur approximation using sliding window over rows and columns
+ */
+public class FastGaussianBlur {
+ /**
+ * Horizontal box blur over rows
+ *
+ * @param source Source image row.
+ * @param target Target image row.
+ * @param width Image width.
+ * @param height Image height.
+ * @param radius Blur radius.
+ */
+ private static void horizontalBoxBlur(int[] source, int[] target, int width, int height, int radius) {
+ double scale = 1.0 / (radius * 2 + 1);
+
+ for (int y = 0; y < height; y++) {
+ int windowSum = 0;
+ int offset = y * width;
+
+ // 1. We initialize the sliding window for the first pixel in the row
+ for (int x = -radius; x <= radius; x++) {
+ int safeX = Math.min(Math.max(x, 0), width - 1);
+ windowSum += source[offset + safeX];
+ }
+
+ // 2. We slide the window across the row
+ for (int x = 0; x < width; x++) {
+ target[offset + x] = (int) Math.round(windowSum * scale);
+
+ // 2a. We subtract the leaving pixel and add the entering pixel
+ int leftX = Math.max(x - radius, 0);
+ int rightX = Math.min(x + radius + 1, width - 1);
+
+ // 2b. We update the sliding window
+ windowSum -= source[offset + leftX];
+ windowSum += source[offset + rightX];
+ }
+ }
+ }
+
+ /**
+ * Vertical box blur over columns. It is identical in logic, but we just
+ * traverse columns instead of rows
+ *
+ * @param source Source image row.
+ * @param target Target image row.
+ * @param width Image width.
+ * @param height Image height.
+ * @param radius Blur radius.
+ */
+ private static void verticalBoxBlur(int[] source, int[] target, int width, int height, int radius) {
+ double scale = 1.0 / (radius * 2 + 1);
+
+ for (int x = 0; x < width; x++) {
+ int windowSum = 0;
+
+ for (int y = -radius; y <= radius; y++) {
+ int safeY = Math.min(Math.max(y, 0), height - 1);
+ windowSum += source[safeY * width + x];
+ }
+
+ for (int y = 0; y < height; y++) {
+ target[y * width + x] = (int) Math.round(windowSum * scale);
+
+ int topY = Math.max(y - radius, 0);
+ int bottomY = Math.min(y + radius + 1, height - 1);
+
+ windowSum -= source[topY * width + x];
+ windowSum += source[bottomY * width + x];
+ }
+ }
+ }
+
+ /**
+ * Main orchestrator to call Fast Gaussian 2D Blur by separating it into
+ * two 1D operations
+ * @param source Source image row.
+ * @param width Image width
+ * @param height Image height
+ * @param radius Blur radius
+ * @param numPasses Number of Passes to Gaussian Approximate
+ */
+ public static int[] applyFastGaussianBlur(int[] source, int width, int height, int radius, int numPasses) {
+ int[] target = new int[source.length];
+ int[] temp = new int[source.length];
+
+ // 1. Copy original image data to target
+ System.arraycopy(source, 0, target, 0, source.length);
+
+ // 2. Run (numPasses = 5) iterations for the Gaussian approximation
+ for (int i = 0; i < numPasses; i++) {
+ horizontalBoxBlur(target, temp, width, height, radius);
+ verticalBoxBlur(temp, target, width, height, radius);
+ }
+ return target;
+ }
+}
diff --git a/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/fastgaussianblur/FastGaussianBlurRealImageTester.java b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/fastgaussianblur/FastGaussianBlurRealImageTester.java
new file mode 100644
index 000000000000..8e1d5a11f56c
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/fastgaussianblur/FastGaussianBlurRealImageTester.java
@@ -0,0 +1,89 @@
+package com.baeldung.algorithms.fastgaussianblur;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.InputStream;
+import javax.imageio.ImageIO;
+
+import javax.annotation.Nonnull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Fast Gaussian Blur approximation tester on a real RGB image
+ */
+public class FastGaussianBlurRealImageTester {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(FastGaussianBlurRealImageTester.class);
+
+ @Nonnull
+ public static BufferedImage blurRealImage(@Nonnull BufferedImage image, int radius, int numPasses) {
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ // 1. We extract 1D array of 32-bit ARGB pixels
+ int[] pixels = image.getRGB(0, 0, width, height, null, 0, width);
+
+ // 2. We define arrays to hold independent color channels
+ int[] a = new int[pixels.length];
+ int[] r = new int[pixels.length];
+ int[] g = new int[pixels.length];
+ int[] b = new int[pixels.length];
+
+ // 3. We unpack the 32-bit integers into separate channels
+ for (int i = 0; i < pixels.length; i++) {
+ a[i] = (pixels[i] >> 24) & 0xff;
+ r[i] = (pixels[i] >> 16) & 0xff;
+ g[i] = (pixels[i] >> 8) & 0xff;
+ b[i] = pixels[i] & 0xff;
+ }
+
+ // 4. We apply our O(n) FastGaussianBlur algorithm to each color channel independently
+ r = FastGaussianBlur.applyFastGaussianBlur(r, width, height, radius, numPasses);
+ g = FastGaussianBlur.applyFastGaussianBlur(g, width, height, radius, numPasses);
+ b = FastGaussianBlur.applyFastGaussianBlur(b, width, height, radius, numPasses);
+
+ // 5. We repack the channels back into a 32-bit integer array
+ int[] resultPixels = new int[pixels.length];
+ for (int i = 0; i < pixels.length; i++) {
+ resultPixels[i] = (a[i] << 24) | (r[i] << 16) | (g[i] << 8) | b[i];
+ }
+
+ // 6. We create a new BufferedImage and set the blurred pixels
+ BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ result.setRGB(0, 0, width, height, resultPixels, 0, width);
+
+ // 7. We return the result
+ return result;
+ }
+
+ public static void main(String[] args) throws Exception {
+ long startTime = System.currentTimeMillis();
+ int radius = 1;
+ int numPasses = 5;
+
+ // 1. We create a thread and read sapmple.jpg from ../src/man/resources
+ InputStream is = Thread.currentThread()
+ .getContextClassLoader()
+ .getResourceAsStream("sample.jpg");
+
+ if (is == null) {
+ throw new RuntimeException("Resource not found: sample.jpg");
+ }
+
+ BufferedImage originalImage = ImageIO.read(is);
+ assert originalImage != null;
+
+ // 2. We run our Fast Blur algorithm
+ BufferedImage blurredImage = blurRealImage(originalImage, radius, numPasses); // Radius 10
+ long endTime = System.currentTimeMillis();
+
+ LOGGER.debug("Blur completed in: " + (endTime - startTime) + " ms");
+
+ //3. We save result image sample_blurred.jpg under algorithms-numeric/target
+ File outputFile = new File("algorithms-numeric/target/sample_blurred.jpg");
+ boolean status = ImageIO.write(blurredImage, "png", outputFile);
+ LOGGER.debug("Blur Operation: " + status + " Saved to: " + outputFile.getAbsolutePath());
+ }
+}
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/frogriverone/FrogRiverOne.java b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/frogriverone/FrogRiverOne.java
new file mode 100644
index 000000000000..859c33dbcc3a
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/frogriverone/FrogRiverOne.java
@@ -0,0 +1,51 @@
+/**
+ * Package to host code for Frog River One coding problem
+ */
+
+package com.baeldung.algorithms.frogriverone;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
+
+public class FrogRiverOne {
+ /*
+ * HashSet and Boolean array based solutions for Frog River One problem
+ * @param m Final destination
+ * @param leaves Integer array to hold leave positions
+ *
+ * @return status Integer to denote total time steps
+ * taken to reach destination
+ */
+ public int HashSetSolution(int m, @Nonnull int[] leaves) {
+ Set leavesCovered = new HashSet<>();
+ int status = -1;
+ for (int k = 0; k < leaves.length; k++) {
+ int position = leaves[k];
+ leavesCovered.add(position);
+
+ if (leavesCovered.size() == m) {
+ status = k + 1;
+ return status;
+ }
+ }
+
+ return status;
+ }
+
+ public int BooleanArraySolution(int m, @Nonnull int[] leaves) {
+ boolean[] leavesCovered = new boolean[m + 1];
+ int leavesUncovered = m;
+ for (int k = 0; k< leaves.length; k++) {
+ int position = leaves[k];
+ if (!leavesCovered[position]) {
+ leavesCovered[position] = true;
+ leavesUncovered--;
+ if (leavesUncovered == 0) {
+ return k + 1;
+ }
+ }
+ }
+ return -1;
+ }
+}
diff --git a/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/sockmerchant/SockMerchant.java b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/sockmerchant/SockMerchant.java
new file mode 100644
index 000000000000..db184cebdce3
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/sockmerchant/SockMerchant.java
@@ -0,0 +1,33 @@
+package com.baeldung.algorithms.sockmerchant;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
+
+public class SockMerchant {
+ public int countPairsWithArray(int n, @Nonnull int[] socks, int k) {
+ int[] counts = new int[k];
+ int pairs = 0;
+ for (int i = 0; i < n; i++) {
+ counts[socks[i]]++;
+ }
+ for (int count : counts) {
+ pairs += count / 2;
+ }
+ return pairs;
+ }
+
+ public int countPairsWithSet(@Nonnull int[] socks) {
+ Set unmatchedSocks = new HashSet<>();
+ int pairs = 0;
+ for (int sock : socks) {
+ if (unmatchedSocks.contains(sock)) {
+ pairs++;
+ unmatchedSocks.remove(sock);
+ } else {
+ unmatchedSocks.add(sock);
+ }
+ }
+ return pairs;
+ }
+}
diff --git a/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/sumoftwosquares/NumberAsSumOfTwoSquares.java b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/sumoftwosquares/NumberAsSumOfTwoSquares.java
new file mode 100644
index 000000000000..2dbf9848efaa
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/sumoftwosquares/NumberAsSumOfTwoSquares.java
@@ -0,0 +1,59 @@
+package com.baeldung.algorithms.sumoftwosquares;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NumberAsSumOfTwoSquares {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NumberAsSumOfTwoSquares.class);
+
+ /**
+ * Checks if a non-negative integer n can be written as the
+ * sum of two squares i.e. (a^2 + b^2)
+ * This implementation is based on Fermat's theorem on sums of two squares.
+ *
+ * @param n The number to check (must be non-negative).
+ * @return true if n can be written as a sum of two squares, false otherwise.
+ */
+ public static boolean isSumOfTwoSquares(int n) {
+ if (n < 0) {
+ LOGGER.warn("Input must be non-negative. Returning false for n = {}", n);
+ return false;
+ }
+ if (n == 0) {
+ return true; // 0 = 0^2 + 0^2
+ }
+
+ // 1. Reduce n to an odd number if n is even.
+ while (n % 2 == 0) {
+ n /= 2;
+ }
+
+ // 2. Iterate through odd prime factors starting from 3
+ for (int i = 3; i * i <= n; i += 2) {
+ // 2a. Find the exponent of the factor i
+ int count = 0;
+ while (n % i == 0) {
+ count++;
+ n /= i;
+ }
+
+ // 2b. Check the condition from Fermat's theorem
+ // If i is of form 4k+3 (i % 4 == 3) and has an odd exponent
+ if (i % 4 == 3 && count % 2 != 0) {
+ LOGGER.debug("Failing condition: factor {} (form 4k+3) has odd exponent {}", i, count);
+ return false;
+ }
+ }
+
+ // 3. Handle the last remaining factor (which is prime if > 1)
+ // If n itself is a prime of the form 4k+3, its exponent is 1 (odd).
+ if (n % 4 == 3) {
+ LOGGER.debug("Failing condition: remaining factor {} is of form 4k+3", n);
+ return false;
+ }
+
+ // 4. All 4k+3 primes had even exponents.
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/twoanglesdifference/AngleDifferenceCalculator.java b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/twoanglesdifference/AngleDifferenceCalculator.java
new file mode 100644
index 000000000000..0edd5d31f377
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/main/java/com/baeldung/algorithms/twoanglesdifference/AngleDifferenceCalculator.java
@@ -0,0 +1,64 @@
+/**
+ * Package to host code for calculating three types of angle difference
+ */
+package com.baeldung.algorithms.twoanglesdifference;
+
+public class AngleDifferenceCalculator {
+
+ /**
+ * Normalizes an angle to be within the range [0, 360).
+ *
+ * @param angle The angle in degrees.
+ * @return The normalized angle.
+ */
+ public static double normalizeAngle(double angle) {
+ return (angle % 360 + 360) % 360;
+ }
+
+ /**
+ * Calculates the absolute difference between two angles.
+ *
+ * @param angle1 The first angle in degrees.
+ * @param angle2 The second angle in degrees.
+ * @return The absolute difference in degrees.
+ */
+ public static double absoluteDifference(double angle1, double angle2) {
+ return Math.abs(angle1 - angle2);
+ }
+
+ /**
+ * Calculates the shortest difference between two angles.
+ *
+ * @param angle1 The first angle in degrees.
+ * @param angle2 The second angle in degrees.
+ * @return The shortest difference in degrees (0 to 180).
+ */
+ public static double shortestDifference(double angle1, double angle2) {
+ double diff = absoluteDifference(normalizeAngle(angle1), normalizeAngle(angle2));
+ return Math.min(diff, 360 - diff);
+ }
+
+ /**
+ * Calculates the signed shortest difference between two angles.
+ * A positive result indicates counter-clockwise rotation, a negative result indicates clockwise.
+ *
+ * @param angle1 The first angle in degrees.
+ * @param angle2 The second angle in degrees.
+ * @return The signed shortest difference in degrees (-180 to 180).
+ */
+ public static double signedShortestDifference(double angle1, double angle2) {
+ double normalizedAngle1 = normalizeAngle(angle1);
+ double normalizedAngle2 = normalizeAngle(angle2);
+ double diff = normalizedAngle2 - normalizedAngle1;
+
+ if (diff > 180) {
+ return diff - 360;
+ } else if (diff < -180) {
+ return diff + 360;
+ } else {
+ return diff;
+ }
+ }
+}
+
+
diff --git a/algorithms-modules/algorithms-numeric/src/main/resources/sample.jpg b/algorithms-modules/algorithms-numeric/src/main/resources/sample.jpg
new file mode 100644
index 000000000000..e7c7b10812c5
Binary files /dev/null and b/algorithms-modules/algorithms-numeric/src/main/resources/sample.jpg differ
diff --git a/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/bcdtodecimal/BCDtoDecimalConverterTest.java b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/bcdtodecimal/BCDtoDecimalConverterTest.java
new file mode 100644
index 000000000000..71fbf161c9fe
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/bcdtodecimal/BCDtoDecimalConverterTest.java
@@ -0,0 +1,95 @@
+package com.baeldung.algorithms.bcdtodecimal;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class BCDtoDecimalConverterTest {
+
+ // 1. Tests for convertPackedByte(byte bcdByte)
+
+ @Test
+ void testConvertPackedByteValidValues() {
+ // Test 05 (0x05) ->
+ assertEquals(5, BCDtoDecimalConverter.convertPackedByte((byte) 0x05));
+
+ // Test 22 (0x22) -> 22
+ assertEquals(22, BCDtoDecimalConverter.convertPackedByte((byte) 0x22));
+
+ // Test 97 (0x97) -> 97
+ assertEquals(97, BCDtoDecimalConverter.convertPackedByte((byte) 0x097));
+ }
+
+ @Test
+ void testConvertPackedByteInvalidUpperNibbleThrowsException() {
+ // Test Upper nibble is A (1010), Lower nibble is 1 (0001) -> 0xA1
+ byte invalidByte = (byte) 0xA1;
+ assertThrows(IllegalArgumentException.class, () -> BCDtoDecimalConverter.convertPackedByte(invalidByte),
+ "Received non-BCD upper nibble (A). Provide valid BCD nibbles (0-9).");
+ }
+
+ @Test
+ void testConvertPackedByteBothInvalidThrowsException() {
+ // test Upper nibble is B, Lower nibble is E -> 0xBE
+ byte invalidByte = (byte) 0xBE;
+ assertThrows(IllegalArgumentException.class,
+ () -> BCDtoDecimalConverter.convertPackedByte(invalidByte),
+ "Received both nibbles as non-BCD. Provide valid BCD nibbles (0-9)."
+ );
+ }
+
+ // -------------------------------------------------------------------------
+
+ // 2. Tests for convertPackedByteArray(byte[] bcdArray)
+
+ @Test
+ void testConvertPackedByteArrayValidValues() {
+ // Test 0 -> [0x00]
+ assertEquals(0L, BCDtoDecimalConverter.convertPackedByteArray(new byte[]{(byte) 0x00}));
+
+ // Test 99 -> [0x99]
+ assertEquals(99L, BCDtoDecimalConverter.convertPackedByteArray(new byte[]{(byte) 0x99}));
+
+ // Test 1234 -> [0x12, 0x34]
+ byte[] bcd1234 = {(byte) 0x12, (byte) 0x34};
+ assertEquals(1234L, BCDtoDecimalConverter.convertPackedByteArray(bcd1234));
+
+ // Test 12345678 -> [0x12, 0x34, 0x56, 0x78]
+ byte[] bcdLarge = {(byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78};
+ assertEquals(12345678L, BCDtoDecimalConverter.convertPackedByteArray(bcdLarge));
+ }
+
+ @Test
+ void testConvertPackedByteArrayEmptyArray() {
+ // Test empty array -> 0
+ assertEquals(0L, BCDtoDecimalConverter.convertPackedByteArray(new byte[]{}));
+ }
+
+ @Test
+ void testConvertPackedByteArrayMaximumSafeLong() {
+ // Test a large number that fits within a long (18 digits)
+ // 999,999,999,999,999,999 (18 nines)
+ byte[] bcdMax = {(byte) 0x99, (byte) 0x99, (byte) 0x99, (byte) 0x99, (byte) 0x99, (byte) 0x99, (byte) 0x99, (byte) 0x99, (byte) 0x99};
+ assertEquals(999999999999999999L, BCDtoDecimalConverter.convertPackedByteArray(bcdMax));
+ }
+
+ @Test
+ void testConvertPackedByteArrayInvalidNibbleThrowsException() {
+ // Contains 0x1A (A is an invalid BCD digit)
+ byte[] bcdInvalid = {(byte) 0x12, (byte) 0x1A, (byte) 0x34};
+ assertThrows(IllegalArgumentException.class,
+ () -> BCDtoDecimalConverter.convertPackedByteArray(bcdInvalid),
+ "Received array containing an invalid BCD byte. Provide valid BCD nibbles (0-9)."
+ );
+ }
+
+ @Test
+ void testConvertPackedByteArray_InvalidFirstByteThrowsException() {
+ // Invalid BCD byte at the start
+ byte[] bcdInvalid = {(byte) 0xF0, (byte) 0x12};
+ assertThrows(IllegalArgumentException.class,
+ () -> BCDtoDecimalConverter.convertPackedByteArray(bcdInvalid),
+ "Received first byte as an invalid BCD byte. Provide valid BCD nibbles (0-9)."
+ );
+ }
+}
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/consecutivenumbers/ConsecutiveNumbersUnitTest.java b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/consecutivenumbers/ConsecutiveNumbersUnitTest.java
new file mode 100644
index 000000000000..110b8c4beabe
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/consecutivenumbers/ConsecutiveNumbersUnitTest.java
@@ -0,0 +1,31 @@
+package com.baeldung.algorithms.consecutivenumbers;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ConsecutiveNumbersUnitTest {
+
+ @Test
+ void whenIsSumOfConsecutiveUsingBruteForce_thenReturnsTrue() {
+ int n = 15;
+
+ boolean isSumOfConsecutive = false;
+ for (int k = 2; (k * (k - 1)) / 2 < n; k++) {
+ int diff = n - k * (k - 1) / 2;
+ if (diff % k == 0 && diff / k > 0) {
+ isSumOfConsecutive = true;
+ break;
+ }
+ }
+
+ assertTrue(isSumOfConsecutive);
+ }
+
+ @Test
+ void whenIsSumOfConsecutiveUsingBitwise_thenReturnsTrue() {
+ int n = 15;
+ boolean result = (n > 0) && ((n & (n - 1)) != 0);
+ assertTrue(result);
+ }
+
+}
diff --git a/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/cosinesimilarity/CosineSimilarityUnitTest.java b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/cosinesimilarity/CosineSimilarityUnitTest.java
new file mode 100644
index 000000000000..ec7fe65a50bb
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/cosinesimilarity/CosineSimilarityUnitTest.java
@@ -0,0 +1,80 @@
+package com.baeldung.algorithms.cosinesimilarity;
+
+import org.junit.jupiter.api.Test;
+import org.nd4j.linalg.api.ndarray.INDArray;
+import org.nd4j.linalg.api.ops.impl.reduce3.CosineSimilarity;
+import org.nd4j.linalg.factory.Nd4j;
+
+import java.util.Arrays;
+import java.util.stream.IntStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CosineSimilarityUnitTest {
+
+ static final double[] VECTOR_A = {3, 4};
+ static final double[] VECTOR_B = {5, 12};
+ static final double EXPECTED_SIMILARITY = 0.9692307692307692;
+
+ static double calculateCosineSimilarity(double[] vectorA, double[] vectorB) {
+ if (vectorA == null || vectorB == null || vectorA.length != vectorB.length || vectorA.length == 0) {
+ throw new IllegalArgumentException("Vectors must be non-null, non-empty, and of the same length.");
+ }
+ double dotProduct = 0.0;
+ double magnitudeA = 0.0;
+ double magnitudeB = 0.0;
+ for (int i = 0; i < vectorA.length; i++) {
+ dotProduct += vectorA[i] * vectorB[i];
+ magnitudeA += vectorA[i] * vectorA[i];
+ magnitudeB += vectorB[i] * vectorB[i];
+ }
+ double finalMagnitudeA = Math.sqrt(magnitudeA);
+ double finalMagnitudeB = Math.sqrt(magnitudeB);
+ if (finalMagnitudeA == 0.0 || finalMagnitudeB == 0.0) {
+ return 0.0;
+ }
+ return dotProduct / (finalMagnitudeA * finalMagnitudeB);
+ }
+
+ public static double calculateCosineSimilarityWithStreams(double[] vectorA, double[] vectorB) {
+ if (vectorA == null || vectorB == null || vectorA.length != vectorB.length || vectorA.length == 0) {
+ throw new IllegalArgumentException("Vectors must be non-null, non-empty, and of the same length.");
+ }
+
+ double dotProduct = IntStream.range(0, vectorA.length).mapToDouble(i -> vectorA[i] * vectorB[i]).sum();
+ double magnitudeA = Arrays.stream(vectorA).map(v -> v * v).sum();
+ double magnitudeB = IntStream.range(0, vectorA.length).mapToDouble(i -> vectorB[i] * vectorB[i]).sum();
+ double finalMagnitudeA = Math.sqrt(magnitudeA);
+ double finalMagnitudeB = Math.sqrt(magnitudeB);
+ if (finalMagnitudeA == 0.0 || finalMagnitudeB == 0.0) {
+ return 0.0;
+ }
+
+ return dotProduct / (finalMagnitudeA * finalMagnitudeB);
+ }
+
+ @Test
+ void givenTwoHighlySimilarVectors_whenCalculatedNatively_thenReturnsHighSimilarityScore() {
+ double actualSimilarity = calculateCosineSimilarity(VECTOR_A, VECTOR_B);
+ assertEquals(EXPECTED_SIMILARITY, actualSimilarity, 1e-15);
+ }
+
+ @Test
+ void givenTwoHighlySimilarVectors_whenCalculatedNativelyWithStreams_thenReturnsHighSimilarityScore() {
+ double actualSimilarity = calculateCosineSimilarityWithStreams(VECTOR_A, VECTOR_B);
+ assertEquals(EXPECTED_SIMILARITY, actualSimilarity, 1e-15);
+ }
+
+ @Test
+ void givenTwoHighlySimilarVectors_whenCalculatedNativelyWithCommonsMath_thenReturnsHighSimilarityScore() {
+
+ INDArray vec1 = Nd4j.create(VECTOR_A);
+ INDArray vec2 = Nd4j.create(VECTOR_B);
+
+ CosineSimilarity cosSim = new CosineSimilarity(vec1, vec2);
+ double actualSimilarity = Nd4j.getExecutioner().exec(cosSim).getDouble(0);
+
+ assertEquals(EXPECTED_SIMILARITY, actualSimilarity, 1e-15);
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/fastgaussianblur/FastGaussianBlurUnitTest.java b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/fastgaussianblur/FastGaussianBlurUnitTest.java
new file mode 100644
index 000000000000..aa8c6ac83c42
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/fastgaussianblur/FastGaussianBlurUnitTest.java
@@ -0,0 +1,32 @@
+package com.baeldung.algorithms.fastgaussianblur;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/**
+ * Fast Gaussian Blur JUnit Test case
+ */
+class FastGaussianBlurUnitTest {
+ @Test
+ void givenSharpImage_whenAppliedBlur_thenCenterIsSmoothed() {
+ int width = 5;
+ int height = 5;
+ int numPasses = 5;
+ int[] image = new int[width * height];
+
+ // 1. We create an impulse image with all indices black image except one bright white pixel in the center.
+ image[12] = 255;
+
+ int[] blurredImage = FastGaussianBlur.applyFastGaussianBlur(image, width, height, 1, numPasses);
+
+ // 2. We expect the center pixel should lose intensity
+ // as it gets spread to its neighbors
+ assertTrue(blurredImage[12] < 255);
+ assertTrue(blurredImage[12] > 0);
+
+ // 3. We expect that its immediate neighbors should have
+ // gained intensity
+ assertTrue(blurredImage[11] > 0); // Left neighbor
+ assertTrue(blurredImage[13] > 0); // Right neighbor
+ }
+}
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/frogriverone/FrogRiverOneUnitTest.java b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/frogriverone/FrogRiverOneUnitTest.java
new file mode 100644
index 000000000000..f9e6a2092fcb
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/frogriverone/FrogRiverOneUnitTest.java
@@ -0,0 +1,39 @@
+/**
+ * Package to host JUNIT5 Unit Test code for Frog River One coding problem
+ */
+
+package com.baeldung.algorithms.frogriverone;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class FrogRiverOneUnitTest {
+
+ private final FrogRiverOne frogRiverOne = new FrogRiverOne();
+
+ @Test
+ void whenLeavesCoverPath_thenReturnsEarliestTime() {
+ int m = 7;
+ int[] leaves = {1, 3, 6, 4, 2, 3, 7, 5, 4};
+ // Expected: Time 8 (Value 5 falls at index 7, completing 1..7)
+
+ // HashSet based solution
+ assertEquals(8, frogRiverOne.HashSetSolution(m, leaves));
+
+ // Boolean array based solution
+ assertEquals(8, frogRiverOne.BooleanArraySolution(m, leaves));
+ }
+
+ @Test
+ void whenLeavesAreMissing_thenReturnsMinusOne() {
+ int m = 7;
+ int[] leaves = {1, 3, 6, 4, 2, 3, 7, 4}; //missing 5
+
+ // HashSet based Solution
+ assertEquals(-1, frogRiverOne.HashSetSolution(m, leaves));
+
+ // Boolean array based solution
+ assertEquals(-1, frogRiverOne.BooleanArraySolution(m, leaves));
+ }
+
+}
diff --git a/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/sockmerchant/SockMerchantUnitTest.java b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/sockmerchant/SockMerchantUnitTest.java
new file mode 100644
index 000000000000..e9bd732cc024
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/sockmerchant/SockMerchantUnitTest.java
@@ -0,0 +1,25 @@
+package com.baeldung.algorithms.sockmerchant;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public class SockMerchantUnitTest {
+ @Test
+ public void givenSockArray_whenUsingArray_thenReturnsCorrectPairCount() {
+ SockMerchant merchant = new SockMerchant();
+ int[] socks = {11, 22, 22, 11, 33, 3, 33, 111111, 33, 222222};
+ int colorMax = 222223;
+ int actualPairs = merchant.countPairsWithArray(socks.length, socks, colorMax);
+ assertEquals(3, actualPairs);
+ }
+
+ @Test
+ public void givenSockArray_whenUsingSet_thenReturnsCorrectPairCount() {
+ SockMerchant merchant = new SockMerchant();
+ int[] socks = {11, 22, 22, 11, 33, 3, 33, 111111, 33, 222222};
+ int actualPairs = merchant.countPairsWithSet(socks);
+ assertEquals(3, actualPairs);
+ }
+}
diff --git a/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/sumoftwosquares/NumberAsSumOfTwoSquaresUnitTest.java b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/sumoftwosquares/NumberAsSumOfTwoSquaresUnitTest.java
new file mode 100644
index 000000000000..ecf2f35efa9b
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/sumoftwosquares/NumberAsSumOfTwoSquaresUnitTest.java
@@ -0,0 +1,47 @@
+package com.baeldung.algorithms.sumoftwosquares;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.DisplayName;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+
+class NumberAsSumOfTwoSquaresUnitTest {
+
+ @Test
+ @DisplayName("Given input number can be expressed as a sum of squares, when checked, then returns true")
+ void givenNumberIsSumOfSquares_whenCheckIsCalled_thenReturnsTrue() {
+ // Simple cases
+ assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(0)); // 0^2 + 0^2
+ assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(1)); // 1^2 + 0^2
+ assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(5)); // 1^2 + 2^2
+ assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(8)); // 2^2 + 2^2
+
+ // Cases from Fermat theorem
+ assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(50)); // 2 * 5^2. No 4k+3 primes.
+ assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(45)); // 3^2 * 5. 4k+3 prime (3) has even exp.
+ assertTrue(NumberAsSumOfTwoSquares.isSumOfTwoSquares(18)); // 2 * 3^2. 4k+3 prime (3) has even exp.
+ }
+
+ @Test
+ @DisplayName("Given input number can't be expressed as a sum of squares, when checked, then returns false")
+ void givenNumberIsNotSumOfSquares_whenCheckIsCalled_thenReturnsFalse() {
+ // Simple cases
+ assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(3)); // 3 (4k+3, exp 1)
+ assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(6)); // 2 * 3 (3 has exp 1)
+ assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(7)); // 7 (4k+3, exp 1)
+ assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(11)); // 11 (4k+3, exp 1)
+
+ // Cases from theorem
+ assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(12)); // 2^2 * 3 (3 has exp 1)
+ assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(21)); // 3 * 7 (both 3 and 7 have exp 1)
+ assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(28)); // 2^2 * 7 (7 has exp 1)
+ }
+
+ @Test
+ @DisplayName("Given input number is negative, when checked, then returns false")
+ void givenNegativeNumber_whenCheckIsCalled_thenReturnsFalse() {
+ assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(-1)); // Negatives as hygiene
+ assertFalse(NumberAsSumOfTwoSquares.isSumOfTwoSquares(-10)); // Negatives as hygiene
+ }
+}
diff --git a/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/twoanglesdifference/AngleDifferenceCalculatorTest.java b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/twoanglesdifference/AngleDifferenceCalculatorTest.java
new file mode 100644
index 000000000000..1539c6b9e37b
--- /dev/null
+++ b/algorithms-modules/algorithms-numeric/src/test/java/com/baeldung/algorithms/twoanglesdifference/AngleDifferenceCalculatorTest.java
@@ -0,0 +1,47 @@
+/**
+ * Package to host JUnit Test code for AngleDifferenceCalculator Class
+ */
+package com.baeldung.algorithms.twoanglesdifference;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+
+
+class AngleDifferenceCalculatorTest {
+
+ private static final double EPSILON = 0.0001;
+
+ @Test
+ void whenNormalizingAngle_thenReturnsCorrectRange() {
+ assertEquals(90, AngleDifferenceCalculator.normalizeAngle(450), EPSILON);
+ assertEquals(30, AngleDifferenceCalculator.normalizeAngle(390), EPSILON);
+ assertEquals(330, AngleDifferenceCalculator.normalizeAngle(-30), EPSILON);
+ assertEquals(0, AngleDifferenceCalculator.normalizeAngle(360), EPSILON);
+ }
+
+ @Test
+ void whenCalculatingAbsoluteDifference_thenReturnsCorrectValue() {
+ assertEquals(100, AngleDifferenceCalculator.absoluteDifference(10, 110), EPSILON);
+ assertEquals(290, AngleDifferenceCalculator.absoluteDifference(10, 300), EPSILON);
+ assertEquals(30, AngleDifferenceCalculator.absoluteDifference(-30, 0), EPSILON);
+ }
+
+ @Test
+ void whenCalculatingShortestDifference_thenReturnsCorrectValue() {
+ assertEquals(100, AngleDifferenceCalculator.shortestDifference(10, 110), EPSILON);
+ assertEquals(70, AngleDifferenceCalculator.shortestDifference(10, 300), EPSILON);
+ assertEquals(30, AngleDifferenceCalculator.shortestDifference(-30, 0), EPSILON);
+ assertEquals(0, AngleDifferenceCalculator.shortestDifference(360, 0), EPSILON);
+ }
+
+ @Test
+ void whenCalculatingSignedShortestDifference_thenReturnsCorrectValue() {
+ assertEquals(100, AngleDifferenceCalculator.signedShortestDifference(10, 110), EPSILON);
+ assertEquals(-70, AngleDifferenceCalculator.signedShortestDifference(10, 300), EPSILON);
+ assertEquals(30, AngleDifferenceCalculator.signedShortestDifference(-30, 0), EPSILON);
+ assertEquals(70, AngleDifferenceCalculator.signedShortestDifference(300, 10), EPSILON);
+ }
+}
+
diff --git a/algorithms-modules/algorithms-optimization/.gitignore b/algorithms-modules/algorithms-optimization/.gitignore
new file mode 100644
index 000000000000..30b2b7442c55
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/.gitignore
@@ -0,0 +1,4 @@
+/target/
+.settings/
+.classpath
+.project
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-optimization/pom.xml b/algorithms-modules/algorithms-optimization/pom.xml
new file mode 100644
index 000000000000..e6d1f07e70e1
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/pom.xml
@@ -0,0 +1,58 @@
+
+
+ 4.0.0
+ algorithms-optimization
+ 0.0.1-SNAPSHOT
+ algorithms-optimization
+
+
+ com.baeldung
+ algorithms-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.apache.commons
+ commons-math3
+ ${commons-math3.version}
+
+
+ commons-codec
+ commons-codec
+ ${commons-codec.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+ org.ojalgo
+ ojalgo
+ ${ojalgo.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ 17
+
+
+
+
+
+
+ 3.6.1
+ 56.2.0
+
+
+
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolution.java b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolution.java
new file mode 100644
index 000000000000..b1bd86d07f0f
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolution.java
@@ -0,0 +1,47 @@
+package com.baeldung.algorithms.optimization.lp;
+
+public class AssignmentSolution {
+
+ private double totalCost;
+ private double[][] assignment;
+
+ public AssignmentSolution(double totalCost, double[][] assignment) {
+ this.totalCost = totalCost;
+ this.assignment = assignment;
+ }
+
+ public double getTotalCost() {
+ return totalCost;
+ }
+
+ public void setTotalCost(double totalCost) {
+ this.totalCost = totalCost;
+ }
+
+ public double[][] getAssignment() {
+ return assignment;
+ }
+
+ public void setAssignment(double[][] assignment) {
+ this.assignment = assignment;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("AssignmentSolution\n");
+ sb.append("Total Cost: ").append(totalCost).append("\n");
+ sb.append("Assignment Matrix:\n");
+ for (int i = 0; i < assignment.length; i++) {
+ sb.append("[ ");
+ for (int j = 0; j < assignment[i].length; j++) {
+ sb.append(String.format("%.0f", assignment[i][j]));
+ if (j < assignment[i].length - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append(" ]\n");
+ }
+ return sb.toString();
+ }
+}
diff --git a/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolver.java b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolver.java
new file mode 100644
index 000000000000..86422b398208
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/AssignmentSolver.java
@@ -0,0 +1,7 @@
+package com.baeldung.algorithms.optimization.lp;
+
+public interface AssignmentSolver {
+
+ AssignmentSolution solve(double[][] cost);
+
+}
diff --git a/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/CommonsMathAssignmentSolver.java b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/CommonsMathAssignmentSolver.java
new file mode 100644
index 000000000000..a7f354a05666
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/CommonsMathAssignmentSolver.java
@@ -0,0 +1,80 @@
+package com.baeldung.algorithms.optimization.lp;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.optim.linear.LinearConstraint;
+import org.apache.commons.math3.optim.linear.LinearConstraintSet;
+import org.apache.commons.math3.optim.linear.LinearObjectiveFunction;
+import org.apache.commons.math3.optim.linear.NonNegativeConstraint;
+import org.apache.commons.math3.optim.linear.Relationship;
+import org.apache.commons.math3.optim.linear.SimplexSolver;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+
+public class CommonsMathAssignmentSolver implements AssignmentSolver {
+
+ public AssignmentSolution solve(double[][] t) {
+
+ int volunteers = t.length;
+ int locations = t[0].length;
+ int vars = volunteers * locations;
+
+ // Objective function coefficients
+ double[] x = new double[vars];
+ for (int i = 0; i < volunteers; i++) {
+ for (int j = 0; j < locations; j++) {
+ x[index(i, j, locations)] = t[i][j];
+ }
+ }
+
+ LinearObjectiveFunction objective = new LinearObjectiveFunction(x, 0);
+
+ Collection constraints = new ArrayList<>();
+
+ // Each volunteer assigned to exactly one location
+ for (int i = 0; i < volunteers; i++) {
+ double[] x_i = new double[vars];
+ for (int j = 0; j < locations; j++) {
+ x_i[index(i, j, locations)] = 1.0;
+ }
+ constraints.add(new LinearConstraint(x_i, Relationship.EQ, 1.0));
+ }
+
+ // Each location gets exactly one volunteer
+ for (int j = 0; j < locations; j++) {
+ double[] x_j = new double[vars];
+ for (int i = 0; i < volunteers; i++) {
+ x_j[index(i, j, locations)] = 1.0;
+ }
+ constraints.add(new LinearConstraint(x_j, Relationship.EQ, 1.0));
+ }
+
+ // Solve LP
+ SimplexSolver solver = new SimplexSolver();
+ PointValuePair solution = solver.optimize(
+ objective,
+ new LinearConstraintSet(constraints),
+ GoalType.MINIMIZE,
+ new NonNegativeConstraint(true)
+ );
+
+ double totalCost = solution.getValue();
+ double[] point = solution.getPoint();
+
+ // Rebuild assignment matrix
+ double[][] assignment = new double[volunteers][locations];
+ for (int i = 0; i < volunteers; i++) {
+ for (int j = 0; j < locations; j++) {
+ assignment[i][j] = point[index(i, j, locations)];
+ }
+ }
+
+ return new AssignmentSolution(totalCost, assignment);
+ }
+
+ private int index(int i, int j, int locations) {
+ return i * locations + j;
+ }
+
+}
diff --git a/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/OjAlgoAssignmentSolver.java b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/OjAlgoAssignmentSolver.java
new file mode 100644
index 000000000000..1097e9cae786
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/main/java/com/baeldung/algorithms/optimization/lp/OjAlgoAssignmentSolver.java
@@ -0,0 +1,57 @@
+package com.baeldung.algorithms.optimization.lp;
+
+import org.ojalgo.optimisation.ExpressionsBasedModel;
+import org.ojalgo.optimisation.Expression;
+import org.ojalgo.optimisation.Variable;
+
+public class OjAlgoAssignmentSolver implements AssignmentSolver {
+
+ public AssignmentSolution solve(double[][] t) {
+
+ int volunteers = t.length;
+ int locations = t[0].length;
+
+ ExpressionsBasedModel model = new ExpressionsBasedModel();
+ Variable[][] x = new Variable[volunteers][locations];
+
+ // Create binary decision variables
+ for (int i = 0; i < volunteers; i++) {
+ for (int j = 0; j < locations; j++) {
+ x[i][j] = model
+ .newVariable("Assignment_" + i + "_" + j)
+ .binary()
+ .weight(t[i][j]);
+ }
+ }
+
+ // Each volunteer is assigned to exactly one location
+ for (int i = 0; i < volunteers; i++) {
+ Expression volunteerConstraint = model.addExpression("Volunteer_" + i).level(1);
+ for (int j = 0; j < locations; j++) {
+ volunteerConstraint.set(x[i][j], 1);
+ }
+ }
+
+ // Each location gets exactly one volunteer
+ for (int j = 0; j < locations; j++) {
+ Expression locationConstraint = model.addExpression("Location_" + j).level(1);
+ for (int i = 0; i < volunteers; i++) {
+ locationConstraint.set(x[i][j], 1);
+ }
+ }
+
+ // Solve
+ var result = model.minimise();
+ double totalCost = result.getValue();
+
+ // Extract assignment matrix
+ double[][] assignment = new double[volunteers][locations];
+ for (int i = 0; i < volunteers; i++) {
+ for (int j = 0; j < locations; j++) {
+ assignment[i][j] = x[i][j].getValue().doubleValue();
+ }
+ }
+
+ return new AssignmentSolution(totalCost, assignment);
+ }
+}
diff --git a/azure/src/main/resources/logback.xml b/algorithms-modules/algorithms-optimization/src/main/resources/logback.xml
similarity index 100%
rename from azure/src/main/resources/logback.xml
rename to algorithms-modules/algorithms-optimization/src/main/resources/logback.xml
diff --git a/algorithms-modules/algorithms-optimization/src/test/java/com/baeldung/algorithms/optimization/lp/AssignmentSolverTest.java b/algorithms-modules/algorithms-optimization/src/test/java/com/baeldung/algorithms/optimization/lp/AssignmentSolverTest.java
new file mode 100644
index 000000000000..df6981b4a82c
--- /dev/null
+++ b/algorithms-modules/algorithms-optimization/src/test/java/com/baeldung/algorithms/optimization/lp/AssignmentSolverTest.java
@@ -0,0 +1,73 @@
+package com.baeldung.algorithms.optimization.lp;
+
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class AssignmentSolverTest {
+
+ @ParameterizedTest
+ @MethodSource("assignmentMatrices")
+ void whenSolveAssignmentMatrixByOjAlgo_thenTheTotalCostIsMinimized(double[][] cost, double expectedTotalCost, double[][] expectedAssignment) {
+ // given
+ AssignmentSolver solver = new OjAlgoAssignmentSolver();
+
+ // when
+ AssignmentSolution solution = solver.solve(cost);
+
+ // then
+ assertThat(solution.getTotalCost()).isEqualTo(expectedTotalCost);
+ assertThat(solution.getAssignment()).isEqualTo(expectedAssignment);
+ }
+
+ @ParameterizedTest
+ @MethodSource("assignmentMatrices")
+ void whenSolveAssignmentMatrixByCommonMaths_thenTheTotalCostIsMinimized(double[][] cost, double expectedTotalCost, double[][] expectedAssignment) {
+ // given
+ AssignmentSolver solver = new CommonsMathAssignmentSolver();
+
+ // when
+ AssignmentSolution solution = solver.solve(cost);
+
+ // then
+ assertThat(solution.getTotalCost()).isEqualTo(expectedTotalCost);
+ assertThat(solution.getAssignment()).isEqualTo(expectedAssignment);
+ }
+
+ static Stream assignmentMatrices() {
+ return Stream.of(
+ Arguments.of(
+ new double[][] {
+ {27, 6, 21},
+ {18, 12, 9},
+ {15, 24, 3}
+ },
+ 27.0,
+ new double[][] {
+ {0, 1, 0},
+ {1, 0, 0},
+ {0, 0, 1}
+ }
+ ),
+ Arguments.of(
+ new double[][] {
+ {9, 2, 7, 8},
+ {6, 4, 3, 7},
+ {5, 8, 1, 8},
+ {7, 6, 9, 4}
+ },
+ 13.0,
+ new double[][] {
+ {0, 1, 0, 0},
+ {1, 0, 0, 0},
+ {0, 0, 1, 0},
+ {0, 0, 0, 1}
+ }
+ )
+ );
+ }
+}
diff --git a/algorithms-modules/algorithms-searching/README.md b/algorithms-modules/algorithms-searching/README.md
deleted file mode 100644
index 394d14a06cc2..000000000000
--- a/algorithms-modules/algorithms-searching/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Algorithms - Searching
-
-This module contains articles about searching algorithms.
-
-### Relevant articles:
-
-- [Binary Search Algorithm in Java](https://www.baeldung.com/java-binary-search)
-- [Depth First Search in Java](https://www.baeldung.com/java-depth-first-search)
-- [Interpolation Search in Java](https://www.baeldung.com/java-interpolation-search)
-- [Breadth-First Search Algorithm in Java](https://www.baeldung.com/java-breadth-first-search)
-- [String Search Algorithms for Large Texts with Java](https://www.baeldung.com/java-full-text-search-algorithms)
-- [Monte Carlo Tree Search for Tic-Tac-Toe Game in Java](https://www.baeldung.com/java-monte-carlo-tree-search)
-- [Range Search Algorithm in Java](https://www.baeldung.com/java-range-search)
-- [Fast Pattern Matching of Strings Using Suffix Tree in Java](https://www.baeldung.com/java-pattern-matching-suffix-tree)
-- [Find the Kth Smallest Element in Two Sorted Arrays in Java](https://www.baeldung.com/java-kth-smallest-element-in-sorted-arrays)
-- [Find the First Non-repeating Element of a List](https://www.baeldung.com/java-list-find-first-non-repeating-element)
diff --git a/algorithms-modules/algorithms-sorting-2/README.md b/algorithms-modules/algorithms-sorting-2/README.md
deleted file mode 100644
index 8e729c48d457..000000000000
--- a/algorithms-modules/algorithms-sorting-2/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-### Relevant Articles:
-
-- [Sorting Strings by Contained Numbers in Java](https://www.baeldung.com/java-sort-strings-contained-numbers)
-- [Partitioning and Sorting Arrays with Many Repeated Entries with Java Examples](https://www.baeldung.com/java-sorting-arrays-with-repeated-entries)
-- [Selection Sort in Java](https://www.baeldung.com/java-selection-sort)
-- [Bubble Sort in Java](https://www.baeldung.com/java-bubble-sort)
-- [Insertion Sort in Java](https://www.baeldung.com/java-insertion-sort)
-- [Heap Sort in Java](https://www.baeldung.com/java-heap-sort)
-- [Counting Sort in Java](https://www.baeldung.com/java-counting-sort)
-- [Radix Sort in Java](https://www.baeldung.com/java-radix-sort)
-- More articles: [[<-- prev]](/algorithms-modules/algorithms-sorting)[[next -->]](/algorithms-modules/algorithms-sorting-3)
diff --git a/algorithms-modules/algorithms-sorting-3/README.md b/algorithms-modules/algorithms-sorting-3/README.md
deleted file mode 100644
index 44db9a83a8f5..000000000000
--- a/algorithms-modules/algorithms-sorting-3/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-### Relevant Articles:
-
-- [Bucket Sort in Java](https://www.baeldung.com/java-bucket-sort)
-- [Shell Sort in Java](https://www.baeldung.com/java-shell-sort)
-- [Gravity/Bead Sort in Java](https://www.baeldung.com/java-gravity-bead-sort)
-- [Guide to In-Place Sorting Algorithm Works with a Java Implementation](https://www.baeldung.com/java-in-place-sorting)
-- More articles: [[<-- prev]](/algorithms-modules/algorithms-sorting-2)
diff --git a/algorithms-modules/algorithms-sorting-3/pom.xml b/algorithms-modules/algorithms-sorting-3/pom.xml
index 0440db1c8c61..40db51a5a30c 100644
--- a/algorithms-modules/algorithms-sorting-3/pom.xml
+++ b/algorithms-modules/algorithms-sorting-3/pom.xml
@@ -1,7 +1,7 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
algorithms-sorting-3
0.0.1-SNAPSHOT
@@ -13,7 +13,4 @@
1.0.0-SNAPSHOT
-
-
-
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-sorting/README.md b/algorithms-modules/algorithms-sorting/README.md
deleted file mode 100644
index 0a9945017f99..000000000000
--- a/algorithms-modules/algorithms-sorting/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-## Algorithms - Sorting
-
-This module contains articles about sorting algorithms.
-
-### Relevant articles:
-- [Merge Sort in Java](https://www.baeldung.com/java-merge-sort)
-- [Quicksort Algorithm Implementation in Java](https://www.baeldung.com/java-quicksort)
-- [Sorting a String Alphabetically in Java](https://www.baeldung.com/java-sort-string-alphabetically)
-- More articles: [[next -->]](/algorithms-modules/algorithms-sorting-2)
diff --git a/algorithms-modules/algorithms-sorting/src/main/java/com/baeldung/algorithms/stringsort/AlphanumericSort.java b/algorithms-modules/algorithms-sorting/src/main/java/com/baeldung/algorithms/stringsort/AlphanumericSort.java
new file mode 100644
index 000000000000..41e4c0919fd9
--- /dev/null
+++ b/algorithms-modules/algorithms-sorting/src/main/java/com/baeldung/algorithms/stringsort/AlphanumericSort.java
@@ -0,0 +1,38 @@
+package com.baeldung.algorithms.stringsort;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class AlphanumericSort {
+
+ public static String lexicographicSort(String input) {
+ char[] stringChars = input.toCharArray();
+ Arrays.sort(stringChars);
+ return new String(stringChars);
+ }
+
+ public static String[] naturalAlphanumericSort(String[] arrayToSort) {
+ Arrays.sort(arrayToSort, new Comparator() {
+ @Override
+ public int compare(String s1, String s2) {
+ return extractInt(s1) - extractInt(s2);
+ }
+
+ private int extractInt(String str) {
+ String num = str.replaceAll("\\D+", "");
+ return num.isEmpty() ? 0 : Integer.parseInt(num);
+ }
+ });
+ return arrayToSort;
+ }
+
+ public static String[] naturalAlphanumericCaseInsensitiveSort(String[] arrayToSort) {
+ Arrays.sort(arrayToSort, Comparator.comparing((String s) -> s.replaceAll("\\d", "").toLowerCase())
+ .thenComparingInt(s -> {
+ String num = s.replaceAll("\\D+", "");
+ return num.isEmpty() ? 0 : Integer.parseInt(num);
+ })
+ .thenComparing(Comparator.naturalOrder()));
+ return arrayToSort;
+ }
+}
\ No newline at end of file
diff --git a/algorithms-modules/algorithms-sorting/src/test/java/com/baeldung/algorithms/stringsort/AlphanumericSortUnitTest.java b/algorithms-modules/algorithms-sorting/src/test/java/com/baeldung/algorithms/stringsort/AlphanumericSortUnitTest.java
new file mode 100644
index 000000000000..15ca81c4480b
--- /dev/null
+++ b/algorithms-modules/algorithms-sorting/src/test/java/com/baeldung/algorithms/stringsort/AlphanumericSortUnitTest.java
@@ -0,0 +1,31 @@
+package com.baeldung.algorithms.stringsort;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class AlphanumericSortUnitTest {
+
+ @Test
+ void givenAlphanumericString_whenLexicographicSort_thenSortLettersFirst() {
+ String stringToSort = "C4B3A21";
+ String sorted = AlphanumericSort.lexicographicSort(stringToSort);
+ assertThat(sorted).isEqualTo("1234ABC");
+ }
+
+ @Test
+ void givenAlphanumericArrayOfStrings_whenAlphanumericSort_thenSortNaturalOrder() {
+ String[] arrayToSort = {"file2", "file10", "file0", "file1", "file20"};
+ String[] sorted = AlphanumericSort.naturalAlphanumericSort(arrayToSort);
+ assertThat(Arrays.toString(sorted)).isEqualTo("[file0, file1, file2, file10, file20]");
+ }
+
+ @Test
+ void givenAlphanumericArrayOfStrings_whenAlphanumericCaseInsensitveSort_thenSortNaturalOrder() {
+ String[] arrayToSort = {"a2", "A10", "b1", "B3", "A2"};
+ String[] sorted = AlphanumericSort.naturalAlphanumericCaseInsensitiveSort(arrayToSort);
+ assertThat(Arrays.toString(sorted)).isEqualTo("[A2, a2, A10, b1, B3]");
+ }
+}
diff --git a/algorithms-modules/pom.xml b/algorithms-modules/pom.xml
index 8c84352c254d..751308d56b6f 100644
--- a/algorithms-modules/pom.xml
+++ b/algorithms-modules/pom.xml
@@ -4,8 +4,8 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
algorithms-modules
- algorithms-modules
pom
+ algorithms-modules
com.baeldung
@@ -26,6 +26,7 @@
algorithms-miscellaneous-9
algorithms-miscellaneous-10
algorithms-numeric
+ algorithms-optimization
algorithms-searching
algorithms-sorting
algorithms-sorting-2
@@ -37,6 +38,7 @@
3.6.1
2.7
1.0.1
+ 3.3.3
diff --git a/apache-cxf-modules/cxf-aegis/README.md b/apache-cxf-modules/cxf-aegis/README.md
deleted file mode 100644
index 1cdb6efbb581..000000000000
--- a/apache-cxf-modules/cxf-aegis/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-### Relevant Articles:
-
-- [Introduction to Apache CXF Aegis Data Binding](https://www.baeldung.com/aegis-data-binding-in-apache-cxf)
diff --git a/apache-cxf-modules/cxf-introduction/README.md b/apache-cxf-modules/cxf-introduction/README.md
deleted file mode 100644
index 3eef16778513..000000000000
--- a/apache-cxf-modules/cxf-introduction/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-### Relevant Articles:
-- [Introduction to Apache CXF](https://www.baeldung.com/introduction-to-apache-cxf)
diff --git a/apache-cxf-modules/cxf-introduction/pom.xml b/apache-cxf-modules/cxf-introduction/pom.xml
index 5e68bf3cbf5f..1aa3a1ea271b 100644
--- a/apache-cxf-modules/cxf-introduction/pom.xml
+++ b/apache-cxf-modules/cxf-introduction/pom.xml
@@ -47,9 +47,4 @@
-