Skip to content

Instantly share code, notes, and snippets.

@mwgamera
Created July 17, 2011 01:56
Show Gist options
  • Save mwgamera/1087019 to your computer and use it in GitHub Desktop.
Save mwgamera/1087019 to your computer and use it in GitHub Desktop.
Alphanumeric derivative of Verhoeff check digit scheme in Java
/**
* 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