diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/FoldingHash.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/FoldingHash.java new file mode 100644 index 0000000000..0ea128c1d9 --- /dev/null +++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/FoldingHash.java @@ -0,0 +1,78 @@ +package com.baeldung.folding; + +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Calculate a hash value for the strings using the folding technique. + * + * The implementation serves only to the illustration purposes and is far + * from being the most efficient. + * + * @author A.Shcherbakov + * + */ +public class FoldingHash { + + /** + * Calculate the hash value of a given string. + * + * @param str Assume it is not null + * @param groupSize the group size in the folding technique + * @param maxValue defines a max value that the hash may acquire (exclusive) + * @return integer value from 0 (inclusive) to maxValue (exclusive) + */ + public int hash(String str, int groupSize, int maxValue) { + final int[] codes = this.toAsciiCodes(str); + return IntStream.range(0, str.length()) + .filter(i -> i % groupSize == 0) + .mapToObj(i -> extract(codes, i, groupSize)) + .map(block -> concatenate(block)) + .reduce(0, (a, b) -> (a + b) % maxValue); + } + + /** + * Returns a new array of given length whose elements are take from + * the original one starting from the offset. + * + * If the original array has not enough elements, the returning array will contain + * element from the offset till the end of the original array. + * + * @param numbers original array. Assume it is not null. + * @param offset index of the element to start from. Assume it is less than the size of the array + * @param length max size of the resulting array + * @return + */ + public int[] extract(int[] numbers, int offset, int length) { + final int defect = numbers.length - (offset + length); + final int s = defect < 0 ? length + defect : length; + int[] result = new int[s]; + for (int index = 0; index < s; index++) { + result[index] = numbers[index + offset]; + } + return result; + } + + /** + * Concatenate the numbers into a single number as if they were strings. + * Assume that the procedure does not suffer from the overflow. + * @param numbers integers to concatenate + * @return + */ + public int concatenate(int[] numbers) { + final String merged = IntStream.of(numbers) + .mapToObj(number -> "" + number) + .collect(Collectors.joining()); + return Integer.parseInt(merged, 10); + } + + /** + * Convert the string into its characters' ASCII codes. + * @param str input string + * @return + */ + private int[] toAsciiCodes(String str) { + return str.chars() + .toArray(); + } +} diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/Main.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/Main.java new file mode 100644 index 0000000000..3b055a0dbe --- /dev/null +++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/folding/Main.java @@ -0,0 +1,17 @@ +package com.baeldung.folding; + +/** + * Code snippet for article "A Guide to the Folding Technique". + * + * @author A.Shcherbakov + * + */ +public class Main { + + public static void main(String... arg) { + FoldingHash hasher = new FoldingHash(); + final String str = "Java language"; + System.out.println(hasher.hash(str, 2, 100_000)); + System.out.println(hasher.hash(str, 3, 1_000)); + } +} diff --git a/algorithms-miscellaneous-3/src/test/java/com/baeldung/folding/FoldingHashUnitTest.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/folding/FoldingHashUnitTest.java new file mode 100644 index 0000000000..43e33d8378 --- /dev/null +++ b/algorithms-miscellaneous-3/src/test/java/com/baeldung/folding/FoldingHashUnitTest.java @@ -0,0 +1,54 @@ +package com.baeldung.folding; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class FoldingHashUnitTest { + + @Test + public void givenStringJavaLanguage_whenSize2Capacity100000_then48933() throws Exception { + final FoldingHash hasher = new FoldingHash(); + final int value = hasher.hash("Java language", 2, 100_000); + assertEquals(value, 48933); + } + + @Test + public void givenStringVaJaLanguage_whenSize2Capacity100000_thenSameAsJavaLanguage() throws Exception { + final FoldingHash hasher = new FoldingHash(); + final int java = hasher.hash("Java language", 2, 100_000); + final int vaja = hasher.hash("vaJa language", 2, 100_000); + assertTrue(java == vaja); + } + + @Test + public void givenSingleElementArray_whenOffset0Size2_thenSingleElement() throws Exception { + final FoldingHash hasher = new FoldingHash(); + final int[] value = hasher.extract(new int[] { 5 }, 0, 2); + assertArrayEquals(new int[] { 5 }, value); + } + + @Test + public void givenFiveElementArray_whenOffset0Size3_thenFirstThreeElements() throws Exception { + final FoldingHash hasher = new FoldingHash(); + final int[] value = hasher.extract(new int[] { 1, 2, 3, 4, 5 }, 0, 3); + assertArrayEquals(new int[] { 1, 2, 3 }, value); + } + + @Test + public void givenFiveElementArray_whenOffset1Size2_thenTwoElements() throws Exception { + final FoldingHash hasher = new FoldingHash(); + final int[] value = hasher.extract(new int[] { 1, 2, 3, 4, 5 }, 1, 2); + assertArrayEquals(new int[] { 2, 3 }, value); + } + + @Test + public void givenFiveElementArray_whenOffset2SizeTooBig_thenElementsToTheEnd() throws Exception { + final FoldingHash hasher = new FoldingHash(); + final int[] value = hasher.extract(new int[] { 1, 2, 3, 4, 5 }, 2, 2000); + assertArrayEquals(new int[] { 3, 4, 5 }, value); + } + +}