Created
July 17, 2011 01:56
-
-
Save mwgamera/1087019 to your computer and use it in GitHub Desktop.
Alphanumeric derivative of Verhoeff check digit scheme in Java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Alphanumeric check digit based on Verhoeff scheme. | |
* A check digit algorithm for alphanumeric data that detects | |
* all single digit errors, all adjacent transpositions, and | |
* over 98% of jump transpositions, twin errors and jump twins. | |
* @author mwgamera | |
**/ | |
public class Ver36 { | |
/** Order of dihedral group used. */ | |
private final static int N = 18; | |
private final static int N2 = 2*N; | |
/** Cayley table for D18 group. */ | |
private static int[][] d18_op = new int[N2][N2]; | |
static { | |
int i, j; | |
for (i = 0; i < N; i++) | |
for (j = 0; j < N; j++) | |
d18_op[i][j] = (i + j) % N; | |
for (i = 0; i < N; i++) | |
for (j = N; j < N2; j++) | |
d18_op[i][j] = N + (i + j) % N; | |
for (i = N; i < N2; i++) | |
for (j = 0; j < N; j++) | |
d18_op[i][j] = N + (i - j) % N; | |
for (i = N; i < N2; i++) | |
for (j = N; j < N2; j++) | |
d18_op[i][j] = (N + i - j) % N; | |
} | |
/** Inverse elements in D18. */ | |
private static int[] d18_inv = new int[N2]; | |
static { | |
int i; | |
d18_inv[0] = 0; | |
for (i = 1; i < N; i++) | |
d18_inv[i] = N - i; | |
for (i = N; i < N2; i++) | |
d18_inv[i] = i; | |
} | |
/** Permutation in cycle-decomposed form. */ | |
private static int[][] perm = new int[N2][]; | |
static { | |
int[] p = { // the permutation | |
29,0,32,11,35,20,7,27,2,4,19,28,30,1,5,12,3,9,16, | |
22,6,33,8,24,26,21,14,10,34,31,15,25,17,13,23,18 | |
}; | |
assert p.length == N2; | |
boolean[] b = new boolean[p.length]; | |
int i = 0; | |
while (i < b.length) { | |
Object[] h = new Object[1]; | |
Object[] c = h; | |
int l = 0; | |
while (!b[i]) { | |
Object[] cc = new Object[2]; | |
cc[1] = new int[]{ p[i] }; | |
c[0] = cc; c = cc; | |
b[i] = true; | |
i = p[i]; | |
l++; | |
} | |
c[0] = h[0]; | |
for (int j = 0; j < l; j++) { | |
int pp = ((int[])c[1])[0]; | |
perm[pp] = new int[l]; | |
for (int k = 0; k < l; k++) { | |
perm[pp][k] = ((int[])c[1])[0]; | |
c = (Object[]) c[0]; | |
} | |
c = (Object[]) c[0]; | |
} | |
for (i = 0; i < b.length && b[i]; i++); | |
} | |
} | |
/** Alphanumeric to numeric conversion table. */ | |
private static int[] a2i = new int[128]; | |
static { | |
int i; | |
for (i = 0x30; i < 0x3a; i++) a2i[i] = 1 + i - 0x30; | |
for (i = 0x41; i < 0x5b; i++) a2i[i] = 1 + i - 0x41+10; | |
for (i = 0x61; i < 0x7b; i++) a2i[i] = 1 + i - 0x61+10; | |
} | |
/** Numbers to alphanumeric conversion table. */ | |
private static char[] i2a = new char[N2]; | |
static { | |
int i; | |
for (i = 0; i < 10; i++) i2a[i] = (char) (i + 0x30); | |
for (i = 10; i < N2; i++) i2a[i] = (char) (i + 0x41-10); | |
} | |
/** | |
* Verify correctness of check digit in alphanumeric String. | |
* Not alphanumeric characters are ignored so given string | |
* can contain some separators or spacing characters. | |
* @param str String to check. | |
* @return True if check digit correct. | |
**/ | |
public static boolean verify(String str) { | |
return verify(str.toCharArray()); | |
} | |
/** | |
* Verify correctness of check digit in alphanumeric character data. | |
* @param data Data to check. | |
* @return True if check digit is correct. | |
**/ | |
public static boolean verify(char[] data) { | |
int i = 0, c = 0, l = data.length; | |
while (l-- != 0) | |
if (a2i[data[l]&0x7f] != 0) { | |
int[] p = perm[a2i[data[l] & 0x7f]-1]; | |
c = d18_op[c][p[i++ % p.length]]; | |
} | |
return c == 0; | |
} | |
/** | |
* Create new check digit. | |
* Character that appended to given string | |
* will make a correct check digit. | |
* @param str String data. | |
* @return Check digit. | |
**/ | |
public static char create(String str) { | |
return create(str.toCharArray()); | |
} | |
/** | |
* Create new check digit. | |
* Character that appended to given character | |
* data will make a correct check digit. | |
* @param data Data. | |
* @return Check digit. | |
**/ | |
public static char create(char[] data) { | |
int i = 1, c = 0, l = data.length; | |
while (l-- != 0) | |
if (a2i[data[l]&0x7f] != 0) { | |
int[] p = perm[a2i[data[l] & 0x7f]-1]; | |
c = d18_op[c][p[i++ % p.length]]; | |
} | |
return i2a[d18_inv[c]]; | |
} | |
/** | |
* Append check digit to given string. | |
* @param str Data to protect with check digit. | |
* @return New String with correct check digit. | |
**/ | |
public static String append(String str) { | |
return str + create(str); | |
} | |
/** Example utility that allows verification and addition of check digits. */ | |
public static void main(String...args) { | |
boolean literal = false; | |
boolean append = false; | |
for (String arg : args) { | |
if (!literal) { | |
if (arg.equals("-a")) { append = true; continue; } | |
if (arg.equals("-c")) { append = false; continue; } | |
if (arg.equals("--")) { literal = true; continue; } | |
} | |
if (append) | |
System.out.println(Ver36.append(arg)); | |
else | |
System.out.println(arg+": "+(Ver36.verify(arg) ? "OK" : "FAILED")); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment