diff --git a/algorithms-modules/algorithms-miscellaneous-7/README.md b/algorithms-modules/algorithms-miscellaneous-7/README.md new file mode 100644 index 0000000000..d220afd678 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- More articles: [[<-- prev]](/algorithms-miscellaneous-6) diff --git a/algorithms-modules/algorithms-miscellaneous-7/pom.xml b/algorithms-modules/algorithms-miscellaneous-7/pom.xml new file mode 100644 index 0000000000..3703ea5a16 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + algorithms-miscellaneous-7 + 0.0.1-SNAPSHOT + algorithms-miscellaneous-7 + + + com.baeldung + algorithms-modules + 1.0.0-SNAPSHOT + + + \ No newline at end of file diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/luhn/LuhnChecker.java b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/luhn/LuhnChecker.java new file mode 100644 index 0000000000..8f8bed4261 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/luhn/LuhnChecker.java @@ -0,0 +1,73 @@ +package com.baeldung.algorithms.luhn; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LuhnChecker { + + private static final Logger LOGGER = LoggerFactory.getLogger(LuhnChecker.class); + + /* + * Starting from the rightmost digit, we add all the digits together, performing + * a special step for every second digit. + * + * If the result is not divisible by 10, then the card number must not be valid. + * + * We can form a number that passes the Luhn Check by subtracting the result of + * the Luhn algorithm from 10. + * + * This is how the final digit of a credit card is calculated. + */ + public static boolean checkLuhn(String cardNumber) { + int sum = 0; + + try { + for (int i = cardNumber.length() - 1; i >= 0; i--) { + int digit = Integer.parseInt(cardNumber.substring(i, i + 1)); + + if ((cardNumber.length() - i) % 2 == 0) { + digit = doubleAndSumDigits(digit); + } + + sum += digit; + } + + LOGGER.info("Luhn Algorithm sum of digits is " + sum); + + } catch (NumberFormatException e) { + LOGGER.error("NumberFormatException - Card number probably contained some non-numeric characters, returning false"); + return false; + } catch (NullPointerException e) { + LOGGER.error("Null pointer - Card number was probably null, returning false"); + return false; + } + + boolean result = sum % 10 == 0; + + LOGGER.info("Luhn check result (sum divisible by 10): " + result); + + return result; + } + + /* + * We apply this method to every second number from the right of the card + * number. First, we double the digit, then we sum the digits. + * + * Note: subtracting 9 is equivalent to doubling and summing digits (when + * starting with a single digit) 0-4 -> produce single digit when doubled + * 5*2 = 10 -> 1+0 = 1 = 10-9 + * 6*2 = 12 -> 1+3 = 3 = 12-9 + * 7*2 = 14 -> 1+5 = 5 = 14-9 + * 8*2 = 16 -> 1+7 = 7 = 16-9 + * 9*2 = 18 -> 1+9 = 9 = 18-9 + */ + public static int doubleAndSumDigits(int digit) { + int ret = digit * 2; + + if (ret > 9) { + ret -= 9; + } + + return ret; + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/main/resources/logback.xml b/algorithms-modules/algorithms-miscellaneous-7/src/main/resources/logback.xml new file mode 100644 index 0000000000..7d900d8ea8 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/luhn/LuhnCheckerUnitTest.java b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/luhn/LuhnCheckerUnitTest.java new file mode 100644 index 0000000000..dd1b184b81 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/luhn/LuhnCheckerUnitTest.java @@ -0,0 +1,63 @@ +package com.baeldung.algorithms.luhn; + +import org.junit.Assert; +import org.junit.Test; + +public class LuhnCheckerUnitTest { + + @Test + public void whenCardNumberDoesMeetLuhnCriteria_thenCheckLuhnReturnsTrue() { + String cardNumber = "8649"; + boolean result = LuhnChecker.checkLuhn(cardNumber); + Assert.assertTrue(result); + } + + @Test + public void whenCardNumberDoesNotMeetLuhnCriteria_thenCheckLuhnReturnsFalse() { + String cardNumber = "8642"; + boolean result = LuhnChecker.checkLuhn(cardNumber); + Assert.assertFalse(result); + } + + @Test + public void whenCardNumberHasNoSecondDigits_thenCheckLuhnCalculatesCorrectly() { + String cardNumber = "0505050505050505"; + boolean result = LuhnChecker.checkLuhn(cardNumber); + Assert.assertTrue(result); + } + + @Test + public void whenCardNumberHasSecondDigits_thenCheckLuhnCalculatesCorrectly() { + String cardNumber = "75757575757575"; + boolean result = LuhnChecker.checkLuhn(cardNumber); + Assert.assertTrue(result); + } + + @Test + public void whenDoubleAndSumDigitsIsCalled_thenOutputIsCorrect() { + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(0), 0); + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(1), 2); + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(2), 4); + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(3), 6); + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(4), 8); + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(5), 1); + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(6), 3); + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(7), 5); + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(8), 7); + Assert.assertEquals(LuhnChecker.doubleAndSumDigits(9), 9); + } + + @Test + public void whenCardNumberNonNumeric_thenCheckLuhnReturnsFalse() { + String cardNumber = "test"; + boolean result = LuhnChecker.checkLuhn(cardNumber); + Assert.assertFalse(result); + } + + @Test + public void whenCardNumberIsNull_thenCheckLuhnReturnsFalse() { + String cardNumber = null; + boolean result = LuhnChecker.checkLuhn(cardNumber); + Assert.assertFalse(result); + } +} diff --git a/algorithms-modules/pom.xml b/algorithms-modules/pom.xml index 4ba819cfe3..4a5f36c944 100644 --- a/algorithms-modules/pom.xml +++ b/algorithms-modules/pom.xml @@ -21,6 +21,7 @@ algorithms-miscellaneous-4 algorithms-miscellaneous-5 algorithms-miscellaneous-6 + algorithms-miscellaneous-7 algorithms-searching algorithms-sorting algorithms-sorting-2