CS-868 Add Luhn Algorithm (#12594)

* CS-868 Add Luhn Algorithm
This commit is contained in:
Shaun Phillips 2022-08-21 15:32:00 +01:00 committed by GitHub
parent 0b9549bd3e
commit 94ecd00052
6 changed files with 169 additions and 0 deletions

View File

@ -0,0 +1,3 @@
### Relevant Articles:
- More articles: [[<-- prev]](/algorithms-miscellaneous-6)

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<modelVersion>4.0.0</modelVersion>
<artifactId>algorithms-miscellaneous-7</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>algorithms-miscellaneous-7</name>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>algorithms-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
</project>

View File

@ -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;
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -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);
}
}

View File

@ -21,6 +21,7 @@
<module>algorithms-miscellaneous-4</module>
<module>algorithms-miscellaneous-5</module>
<module>algorithms-miscellaneous-6</module>
<module>algorithms-miscellaneous-7</module>
<module>algorithms-searching</module>
<module>algorithms-sorting</module>
<module>algorithms-sorting-2</module>