Skip to content

Commit fb626e0

Browse files
authored
Add Luhn algorithm (Fixes: TheAlgorithms#2537) (TheAlgorithms#2538)
1 parent 1d03770 commit fb626e0

1 file changed

Lines changed: 155 additions & 0 deletions

File tree

Others/Luhn.java

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package Others;
2+
3+
import java.util.Arrays;
4+
import java.util.Objects;
5+
6+
/**
7+
* The Luhn algorithm or Luhn formula, also known as the "modulus 10" or "mod 10" algorithm,
8+
* named after its creator, IBM scientist Hans Peter Luhn, is a simple checksum formula
9+
* used to validate a variety of identification numbers.
10+
*
11+
* <p>The algorithm is in the public domain and is in wide use today.
12+
* It is specified in ISO/IEC 7812-1. It is not intended to be a cryptographically
13+
* secure hash function; it was designed to protect against accidental errors,
14+
* not malicious attacks. Most credit cards and many government identification numbers
15+
* use the algorithm as a simple method of distinguishing valid numbers from mistyped or
16+
* otherwise incorrect numbers.</p>
17+
*
18+
* <p>The Luhn algorithm will detect any single-digit error, as well as almost all
19+
* transpositions of adjacent digits. It will not, however, detect transposition of the
20+
* two-digit sequence 09 to 90 (or vice versa). It will detect most of the possible
21+
* twin errors (it will not detect 22 ↔ 55, 33 ↔ 66 or 44 ↔ 77).</p>
22+
*
23+
* <p>The check digit is computed as follows:</p>
24+
* <ol>
25+
* <li>Take the original number and starting from the rightmost digit moving left, double the value of every second digit (including the rightmost digit).</li>
26+
* <li>Replace the resulting value at each position with the sum of the digits of this position's value or just subtract 9 from all numbers more or equal then 10.</li>
27+
* <li>Sum up the resulting values from all positions (s).</li>
28+
* <li>The calculated check digit is equal to {@code 10 - s % 10}.</li>
29+
* </ol>
30+
*
31+
* @see <a href="https://en.wikipedia.org/wiki/Luhn_algorithm">Wiki</a>
32+
*/
33+
public class Luhn {
34+
35+
/**
36+
* Check input digits array by Luhn algorithm.
37+
* Initial array doesn't change while processing.
38+
* @param digits array of digits from 0 to 9
39+
* @return true if check was successful, false otherwise
40+
*/
41+
public static boolean luhnCheck(int[] digits) {
42+
int[] numbers = Arrays.copyOf(digits, digits.length);
43+
int sum = 0;
44+
45+
for (int i = numbers.length - 1; i >= 0; i--) {
46+
if (i % 2 == 0) {
47+
int temp = numbers[i] * 2;
48+
if (temp > 9) {
49+
temp = temp - 9;
50+
}
51+
numbers[i] = temp;
52+
}
53+
sum += numbers[i];
54+
}
55+
56+
return sum % 10 == 0;
57+
}
58+
59+
public static void main(String[] args) {
60+
System.out.println("Luhn algorithm usage examples:");
61+
int[] validInput = {4, 5, 6, 1, 2, 6, 1, 2, 1, 2, 3, 4, 5, 4, 6, 7};
62+
int[] invalidInput = {4, 5, 6, 1, 2, 6, 1, 2, 1, 2, 3, 4, 5, 4, 6, 4}; //typo in last symbol
63+
checkAndPrint(validInput);
64+
checkAndPrint(invalidInput);
65+
66+
System.out.println("\nBusiness examples:");
67+
String validCardNumber = "5265 9251 6151 1412";
68+
String invalidCardNumber = "4929 3231 3088 1896";
69+
String illegalCardNumber = "4F15 BC06 3A88 76D5";
70+
businessExample(validCardNumber);
71+
businessExample(invalidCardNumber);
72+
businessExample(illegalCardNumber);
73+
}
74+
75+
private static void checkAndPrint(int[] input) {
76+
String validationResult = Luhn.luhnCheck(input)
77+
? "valid"
78+
: "not valid";
79+
System.out.println("Input " + Arrays.toString(input) + " is " + validationResult);
80+
}
81+
82+
83+
/*
84+
========================
85+
Business usage example
86+
========================
87+
*/
88+
89+
/**
90+
* Object representation of credit card.
91+
*/
92+
private record CreditCard(int[] digits) {
93+
94+
private static final int DIGITS_COUNT = 16;
95+
96+
/**
97+
* @param cardNumber string representation of credit card number - 16 digits.
98+
* Can have spaces for digits separation
99+
* @return credit card object
100+
* @throws IllegalArgumentException if input string is not 16 digits
101+
* or if Luhn check was failed
102+
*/
103+
public static CreditCard fromString(String cardNumber) {
104+
Objects.requireNonNull(cardNumber);
105+
String trimmedCardNumber = cardNumber.replaceAll(" ", "");
106+
if (trimmedCardNumber.length() != DIGITS_COUNT || !trimmedCardNumber.matches("\\d+")) {
107+
throw new IllegalArgumentException("{" + cardNumber + "} - is not a card number");
108+
}
109+
110+
int[] cardNumbers = toIntArray(trimmedCardNumber);
111+
boolean isValid = luhnCheck(cardNumbers);
112+
if (!isValid) {
113+
throw new IllegalArgumentException("Credit card number {" + cardNumber + "} - have a typo");
114+
}
115+
116+
return new CreditCard(cardNumbers);
117+
}
118+
119+
/**
120+
* @return string representation separated by space every 4 digits.
121+
* Example: "5265 9251 6151 1412"
122+
*/
123+
public String number() {
124+
StringBuilder result = new StringBuilder();
125+
for (int i = 0; i < DIGITS_COUNT; i++) {
126+
if (i % 4 == 0 && i != 0) {
127+
result.append(" ");
128+
}
129+
result.append(digits[i]);
130+
}
131+
return result.toString();
132+
}
133+
134+
@Override
135+
public String toString() {
136+
return String.format("%s {%s}", CreditCard.class.getSimpleName(), number());
137+
}
138+
139+
private static int[] toIntArray(String string) {
140+
return string.chars()
141+
.map(i -> Character.digit(i, 10))
142+
.toArray();
143+
}
144+
}
145+
146+
private static void businessExample(String cardNumber) {
147+
try {
148+
System.out.println("Trying to create CreditCard object from valid card number: " + cardNumber);
149+
CreditCard creditCard = CreditCard.fromString(cardNumber);
150+
System.out.println("And business object is successfully created: " + creditCard + "\n");
151+
} catch (IllegalArgumentException e) {
152+
System.out.println("And fail with exception message: " + e.getMessage() + "\n");
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)