|
| 1 | +package Others; |
| 2 | + |
| 3 | +import java.util.Objects; |
| 4 | + |
| 5 | +/** |
| 6 | + * Damm algorithm is a check digit algorithm that detects all single-digit errors |
| 7 | + * and all adjacent transposition errors. It was presented by H. Michael Damm in 2004. |
| 8 | + * Essential part of the algorithm is a quasigroup of order 10 |
| 9 | + * (i.e. having a 10 × 10 Latin square as the body of its operation table) |
| 10 | + * with the special feature of being weakly totally anti-symmetric. |
| 11 | + * Damm revealed several methods to create totally anti-symmetric quasigroups |
| 12 | + * of order 10 and gave some examples in his doctoral dissertation. |
| 13 | + * |
| 14 | + * @see <a href="https://en.wikipedia.org/wiki/Damm_algorithm">Wiki. Damm algorithm</a> |
| 15 | + */ |
| 16 | +public class Damm { |
| 17 | + |
| 18 | + /** |
| 19 | + * Weakly totally anti-symmetric quasigroup of order 10. |
| 20 | + * This table is not the only possible realisation of weak totally anti-symmetric |
| 21 | + * quasigroup but the most common one (taken from Damm doctoral dissertation). |
| 22 | + * All zeros lay on the diagonal because it simplifies the check digit calculation. |
| 23 | + */ |
| 24 | + private static final byte[][] DAMM_TABLE = { |
| 25 | + {0, 3, 1, 7, 5, 9, 8, 6, 4, 2}, |
| 26 | + {7, 0, 9, 2, 1, 5, 4, 8, 6, 3}, |
| 27 | + {4, 2, 0, 6, 8, 7, 1, 3, 5, 9}, |
| 28 | + {1, 7, 5, 0, 9, 8, 3, 4, 2, 6}, |
| 29 | + {6, 1, 2, 3, 0, 4, 5, 9, 7, 8}, |
| 30 | + {3, 6, 7, 4, 2, 0, 9, 5, 8, 1}, |
| 31 | + {5, 8, 6, 9, 7, 2, 0, 1, 3, 4}, |
| 32 | + {8, 9, 4, 5, 3, 6, 2, 0, 1, 7}, |
| 33 | + {9, 4, 3, 8, 6, 1, 7, 2, 0, 5}, |
| 34 | + {2, 5, 8, 1, 4, 3, 6, 7, 9, 0} |
| 35 | + }; |
| 36 | + |
| 37 | + /** |
| 38 | + * Check input digits by Damm algorithm. |
| 39 | + * |
| 40 | + * @param digits input to check |
| 41 | + * @return true if check was successful, false otherwise |
| 42 | + * @throws IllegalArgumentException if input parameter contains not only digits |
| 43 | + * @throws NullPointerException if input is null |
| 44 | + */ |
| 45 | + public static boolean dammCheck(String digits) { |
| 46 | + checkInput(digits); |
| 47 | + int[] numbers = toIntArray(digits); |
| 48 | + |
| 49 | + int checksum = 0; |
| 50 | + for (int number : numbers) { |
| 51 | + checksum = DAMM_TABLE[checksum][number]; |
| 52 | + } |
| 53 | + |
| 54 | + return checksum == 0; |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * Calculate check digit for initial digits and add it tho the last position. |
| 59 | + * |
| 60 | + * @param initialDigits initial value |
| 61 | + * @return digits with the checksum in the last position |
| 62 | + * @throws IllegalArgumentException if input parameter contains not only digits |
| 63 | + * @throws NullPointerException if input is null |
| 64 | + */ |
| 65 | + public static String addDammChecksum(String initialDigits) { |
| 66 | + checkInput(initialDigits); |
| 67 | + int[] numbers = toIntArray(initialDigits); |
| 68 | + |
| 69 | + int checksum = 0; |
| 70 | + for (int number : numbers) { |
| 71 | + checksum = DAMM_TABLE[checksum][number]; |
| 72 | + } |
| 73 | + |
| 74 | + return initialDigits + checksum; |
| 75 | + } |
| 76 | + |
| 77 | + public static void main(String[] args) { |
| 78 | + System.out.println("Damm algorithm usage examples:"); |
| 79 | + var validInput = "5724"; |
| 80 | + var invalidInput = "5824"; |
| 81 | + checkAndPrint(validInput); |
| 82 | + checkAndPrint(invalidInput); |
| 83 | + |
| 84 | + System.out.println("\nCheck digit generation example:"); |
| 85 | + var input = "572"; |
| 86 | + generateAndPrint(input); |
| 87 | + } |
| 88 | + |
| 89 | + private static void checkAndPrint(String input) { |
| 90 | + String validationResult = Damm.dammCheck(input) |
| 91 | + ? "valid" |
| 92 | + : "not valid"; |
| 93 | + System.out.println("Input '" + input + "' is " + validationResult); |
| 94 | + } |
| 95 | + |
| 96 | + private static void generateAndPrint(String input) { |
| 97 | + String result = addDammChecksum(input); |
| 98 | + System.out.println("Generate and add checksum to initial value '" + input + "'. Result: '" + result + "'"); |
| 99 | + } |
| 100 | + |
| 101 | + private static void checkInput(String input) { |
| 102 | + Objects.requireNonNull(input); |
| 103 | + if (!input.matches("\\d+")) { |
| 104 | + throw new IllegalArgumentException("Input '" + input + "' contains not only digits"); |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + private static int[] toIntArray(String string) { |
| 109 | + return string.chars() |
| 110 | + .map(i -> Character.digit(i, 10)) |
| 111 | + .toArray(); |
| 112 | + } |
| 113 | +} |
0 commit comments