Verhoeff algorithm
The Verhoeff algorithm is a checksum formula for error detection developed by the Dutch mathematician Jacobus Verhoeff and first published in 1969. It was the first decimal check digit algorithm which detects all single-digit errors, and all transposition errors involving two adjacent digits, which was at the time thought impossible with such a code.

You are encouraged to solve this task according to the task description, using any language you may know.
- Description
As the workings of the algorithm are clearly described in the linked Wikipedia article they will not be repeated here.
- Task
Write routines, methods, procedures etc. in your language to generate a Verhoeff checksum digit for non-negative integers of any length and to validate the result. A combined routine is also acceptable.
The more mathematically minded may prefer to generate the 3 tables required from the description provided rather than to hard-code them.
Write your routines in such a way that they can optionally display digit by digit calculations as in the Wikipedia example.
Use your routines to calculate check digits for the integers: 236, 12345 and 123456789012 and then validate them. Also attempt to validate the same integers if the check digits in all cases were 9 rather than what they actually are.
Display digit by digit calculations for the first two integers but not for the third.
- Related task
V MULTIPLICATION_TABLE = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]]
V INV = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]
V PERMUTATION_TABLE = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8]]
F verhoeffchecksum(n, validate = 1B, terse = 1B, verbose = 0B)
‘
Calculate the Verhoeff checksum over `n`.
Terse mode or with single argument: return True if valid (last digit is a correct check digit).
If checksum mode, return the expected correct checksum digit.
If validation mode, return True if last digit checks correctly.
’
I verbose
print(("\n"(I validate {‘Validation’} E ‘Check digit’))‘ ’(‘calculations for ’n":\n\n i ni p[i,ni] c\n------------------"))
V (c, dig) = (0, Array(String(I validate {n} E 10 * n)))
L(ni) reversed(dig)
V i = L.index
V p = :PERMUTATION_TABLE[i % 8][Int(ni)]
c = :MULTIPLICATION_TABLE[c][p]
I verbose
print(f:‘{i:2} {ni} {p} {c}’)
I verbose & !validate
print("\ninv("c‘) = ’:INV[c])
I !terse
print(I validate {"\nThe validation for '"n‘' is ’(I c == 0 {‘correct’} E ‘incorrect’)‘.’} E "\nThe check digit for '"n‘' is ’:INV[c]‘.’)
R I validate {c == 0} E :INV[c]
L(n, va, t, ve) [(Int64(236), 0B, 0B, 1B),
(Int64(2363), 1B, 0B, 1B),
(Int64(2369), 1B, 0B, 1B),
(Int64(12345), 0B, 0B, 1B),
(Int64(123451), 1B, 0B, 1B),
(Int64(123459), 1B, 0B, 1B),
(Int64(123456789012), 0B, 0B, 0B),
(Int64(1234567890120), 1B, 0B, 0B),
(Int64(1234567890129), 1B, 0B, 0B)]
verhoeffchecksum(n, va, t, ve)- Output:
The same as in Python.
Algol 68 doesn't allow INTs to be used as BOOLs, so this has separate verhoeff validation and verhoeff check digit procedures. They both call a common verhoeff algorithm proceedure.
BEGIN # The Verhoeff algorithm - translated from the Wren sample via FreeBASIC #
[,]INT d = ( ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )
, ( 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 )
, ( 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 )
, ( 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 )
, ( 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 )
, ( 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 )
, ( 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 )
, ( 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 )
, ( 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 )
, ( 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 )
);
[ ]INT inv = ( 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 );
[,]INT p = ( ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )
, ( 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 )
, ( 5, 8, 0, 3, 7, 9, 6, 1, 4, 2 )
, ( 8, 9, 1, 6, 0, 4, 3, 5, 2, 7 )
, ( 9, 4, 5, 3, 1, 2, 6, 8, 7, 0 )
, ( 4, 2, 8, 6, 5, 7, 3, 9, 0, 1 )
, ( 2, 7, 9, 3, 8, 0, 6, 4, 1, 5 )
, ( 7, 0, 4, 6, 9, 1, 3, 2, 5, 8 )
);
PROC verhoeff algorithm = ( STRING s in, BOOL validate, table )INT:
BEGIN
IF table THEN
print( ( IF validate THEN "Validation" ELSE "Check digit" FI ) );
print( ( " calculations for '", s in, "':", newline ) );
print( ( " i ni p[i,ni] c", newline, " ------------------", newline ) )
FI;
STRING s = IF validate THEN s in ELSE s in + "0" FI;
INT c := 0;
INT le = UPB s;
FOR k FROM le BY -1 TO LWB s DO
INT nidx = ABS s[ k ] - 48;
INT pidx = p[ 1 + ( le - k ) MOD 8, 1 + nidx ];
c := d[ c + 1, pidx + 1 ];
IF table
THEN print( ( " ", whole( le - k, -2 ), " ", whole( nidx, 0 ), " ", whole( pidx, 0 ) ) );
print( ( " ", whole( c, 0 ), newline ) )
FI
OD;
IF table AND NOT validate
THEN print( ( " inv[", whole( c, 0 ), "] = ", whole( inv[ c + 1 ], 0 ), newline ) )
FI;
IF NOT validate THEN inv[ c + 1 ] ELSE c FI
END # verhoeff algorithm # ;
PROC verhoeff validation = ( STRING s, BOOL table )BOOL: verhoeff algorithm( s, TRUE, table ) = 0;
PROC verhoeff check digit = ( STRING s, BOOL table )INT: verhoeff algorithm( s, FALSE, table );
BEGIN # test cases #
MODE TCASE = STRUCT( STRING s, BOOL table );
[]TCASE sts = ( ( "236", TRUE ), ( "12345", TRUE ), ( "123456789012", FALSE ) );
FOR i FROM LWB sts TO UPB sts DO
STRING s = s OF sts[ i ];
INT c = verhoeff check digit( s, table OF sts[ i ] );
print( ( "The check digit for '", s, "' is '", whole( c, 0 ), "'", newline ) );
[]STRING stc = ( s + REPR ( c + ABS "0" ), s + "9" );
FOR j FROM LWB stc TO UPB stc DO
BOOL v = verhoeff validation( stc[ j ], table OF sts[ i ] );
print( ( "Validation for '", stc[ j ], "' -> " ) );
print( ( IF v THEN "correct" ELSE "incorrect" FI, ".", newline ) )
OD;
print( ( newline, newline ) )
OD
END
END- Output:
Check digit calculations for '236':
i ni p[i,ni] c
------------------
0 0 0 0
1 6 3 3
2 3 3 1
3 2 1 2
inv[2] = 3
The check digit for '236' is '3'
Validation calculations for '2363':
i ni p[i,ni] c
------------------
0 3 3 3
1 6 3 1
2 3 3 4
3 2 1 0
Validation for '2363' -> correct.
Validation calculations for '2369':
i ni p[i,ni] c
------------------
0 9 9 9
1 6 3 6
2 3 3 8
3 2 1 7
Validation for '2369' -> incorrect.
Check digit calculations for '12345':
i ni p[i,ni] c
------------------
0 0 0 0
1 5 8 8
2 4 7 1
3 3 6 7
4 2 5 2
5 1 2 4
inv[4] = 1
The check digit for '12345' is '1'
Validation calculations for '123451':
i ni p[i,ni] c
------------------
0 1 1 1
1 5 8 9
2 4 7 2
3 3 6 8
4 2 5 3
5 1 2 0
Validation for '123451' -> correct.
Validation calculations for '123459':
i ni p[i,ni] c
------------------
0 9 9 9
1 5 8 1
2 4 7 8
3 3 6 2
4 2 5 7
5 1 2 5
Validation for '123459' -> incorrect.
The check digit for '123456789012' is '0'
Validation for '1234567890120' -> correct.
Validation for '1234567890129' -> incorrect.
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
static const int d[][10] = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 2, 3, 4, 0, 6, 7, 8, 9, 5},
{2, 3, 4, 0, 1, 7, 8, 9, 5, 6}, {3, 4, 0, 1, 2, 8, 9, 5, 6, 7},
{4, 0, 1, 2, 3, 9, 5, 6, 7, 8}, {5, 9, 8, 7, 6, 0, 4, 3, 2, 1},
{6, 5, 9, 8, 7, 1, 0, 4, 3, 2}, {7, 6, 5, 9, 8, 2, 1, 0, 4, 3},
{8, 7, 6, 5, 9, 3, 2, 1, 0, 4}, {9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
};
static const int inv[] = {0, 4, 3, 2, 1, 5, 6, 7, 8, 9};
static const int p[][10] = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 5, 7, 6, 2, 8, 3, 0, 9, 4},
{5, 8, 0, 3, 7, 9, 6, 1, 4, 2}, {8, 9, 1, 6, 0, 4, 3, 5, 2, 7},
{9, 4, 5, 3, 1, 2, 6, 8, 7, 0}, {4, 2, 8, 6, 5, 7, 3, 9, 0, 1},
{2, 7, 9, 3, 8, 0, 6, 4, 1, 5}, {7, 0, 4, 6, 9, 1, 3, 2, 5, 8},
};
int verhoeff(const char* s, bool validate, bool verbose) {
if (verbose) {
const char* t = validate ? "Validation" : "Check digit";
printf("%s calculations for '%s':\n\n", t, s);
puts(u8" i n\xE1\xB5\xA2 p[i,n\xE1\xB5\xA2] c");
puts("------------------");
}
int len = strlen(s);
if (validate)
--len;
int c = 0;
for (int i = len; i >= 0; --i) {
int ni = (i == len && !validate) ? 0 : s[i] - '0';
assert(ni >= 0 && ni < 10);
int pi = p[(len - i) % 8][ni];
c = d[c][pi];
if (verbose)
printf("%2d %d %d %d\n", len - i, ni, pi, c);
}
if (verbose && !validate)
printf("\ninv[%d] = %d\n", c, inv[c]);
return validate ? c == 0 : inv[c];
}
int main() {
const char* ss[3] = {"236", "12345", "123456789012"};
for (int i = 0; i < 3; ++i) {
const char* s = ss[i];
bool verbose = i < 2;
int c = verhoeff(s, false, verbose);
printf("\nThe check digit for '%s' is '%d'.\n", s, c);
int len = strlen(s);
char sc[len + 2];
strncpy(sc, s, len + 2);
for (int j = 0; j < 2; ++j) {
sc[len] = (j == 0) ? c + '0' : '9';
int v = verhoeff(sc, true, verbose);
printf("\nThe validation for '%s' is %s.\n", sc,
v ? "correct" : "incorrect");
}
}
return 0;
}
- Output:
Check digit calculations for '236': i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 inv[2] = 3 The check digit for '236' is '3'. Validation calculations for '2363': i nᵢ p[i,nᵢ] c ------------------ 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 The validation for '2363' is correct. Validation calculations for '2369': i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 The validation for '2369' is incorrect. Check digit calculations for '12345': i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 inv[4] = 1 The check digit for '12345' is '1'. Validation calculations for '123451': i nᵢ p[i,nᵢ] c ------------------ 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The validation for '123451' is correct. Validation calculations for '123459': i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 The validation for '123459' is incorrect. The check digit for '123456789012' is '0'. The validation for '1234567890120' is correct. The validation for '1234567890129' is incorrect.
#include <cstdint>
#include <iostream>
#include <string>
#include <array>
#include <iomanip>
typedef std::pair<std::string, bool> data;
const std::array<const std::array<int32_t, 10>, 10> multiplication_table = { {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
{ 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 },
{ 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 },
{ 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 },
{ 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 },
{ 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 },
{ 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 },
{ 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 },
{ 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 },
{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }
} };
const std::array<int32_t, 10> inverse = { 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 };
const std::array<const std::array<int32_t, 10>, 8> permutation_table = { {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
{ 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 },
{ 5, 8, 0, 3, 7, 9, 6, 1, 4, 2 },
{ 8, 9, 1, 6, 0, 4, 3, 5, 2, 7 },
{ 9, 4, 5, 3, 1, 2, 6, 8, 7, 0 },
{ 4, 2, 8, 6, 5, 7, 3, 9, 0, 1 },
{ 2, 7, 9, 3, 8, 0, 6, 4, 1, 5 },
{ 7, 0, 4, 6, 9, 1, 3, 2, 5, 8 }
} };
int32_t verhoeff_checksum(std::string number, const bool doValidation, const bool doDisplay) {
if ( doDisplay ) {
std::string calculationType = doValidation ? "Validation" : "Check digit";
std::cout << calculationType << " calculations for " << number << "\n" << std::endl;
std::cout << " i ni p[i, ni] c" << std::endl;
std::cout << "-------------------" << std::endl;
}
if ( ! doValidation ) {
number += "0";
}
int32_t c = 0;
const int32_t le = number.length() - 1;
for ( int32_t i = le; i >= 0; i-- ) {
const int32_t ni = number[i] - '0';
const int32_t pi = permutation_table[(le - i) % 8][ni];
c = multiplication_table[c][pi];
if ( doDisplay ) {
std::cout << std::setw(2) << le - i << std::setw(3) << ni
<< std::setw(8) << pi << std::setw(6) << c << "\n" << std::endl;
}
}
if ( doDisplay && ! doValidation ) {
std::cout << "inverse[" << c << "] = " << inverse[c] << "\n" << std::endl;;
}
return doValidation ? c == 0 : inverse[c];
}
int main( ) {
const std::array<data, 3> tests = {
std::make_pair("123", true), std::make_pair("12345", true), std::make_pair("123456789012", false) };
for ( const data& test : tests ) {
int32_t digit = verhoeff_checksum(test.first, false, test.second);
std::cout << "The check digit for " << test.first << " is " << digit << "\n" << std::endl;
std::string numbers[2] = { test.first + std::to_string(digit), test.first + "9" };
for ( const std::string& number : numbers ) {
digit = verhoeff_checksum(number, true, test.second);
std::string result = ( digit == 1 ) ? "correct" : "incorrect";
std::cout << "The validation for " << number << " is " << result << ".\n" << std::endl;
}
}
}
- Output:
The same as the Wren example.
d[][] = [ [ 0 1 2 3 4 5 6 7 8 9 ] [ 1 2 3 4 0 6 7 8 9 5 ] [ 2 3 4 0 1 7 8 9 5 6 ] [ 3 4 0 1 2 8 9 5 6 7 ] [ 4 0 1 2 3 9 5 6 7 8 ] [ 5 9 8 7 6 0 4 3 2 1 ] [ 6 5 9 8 7 1 0 4 3 2 ] [ 7 6 5 9 8 2 1 0 4 3 ] [ 8 7 6 5 9 3 2 1 0 4 ] [ 9 8 7 6 5 4 3 2 1 0 ] ]
inv[] = [ 0 4 3 2 1 5 6 7 8 9 ]
p[][] = [ [ 0 1 2 3 4 5 6 7 8 9 ] [ 1 5 7 6 2 8 3 0 9 4 ] [ 5 8 0 3 7 9 6 1 4 2 ] [ 8 9 1 6 0 4 3 5 2 7 ] [ 9 4 5 3 1 2 6 8 7 0 ] [ 4 2 8 6 5 7 3 9 0 1 ] [ 2 7 9 3 8 0 6 4 1 5 ] [ 7 0 4 6 9 1 3 2 5 8 ] ]
#
func verhoeff s$ validate verbose .
if verbose = 1
t$ = "Check digit"
if validate = 1 : t$ = "Validation"
print t$ & " calculations for '" & s$ & "'\n"
print " i nᵢ p[i,nᵢ] c"
print "------------------"
.
s$[] = strchars s$
lng = len s$[]
if validate = 1 : lng -= 1
for i = lng downto 0
ni = 0
if i < lng or validate = 1 : ni = strcode s$[i + 1] - 48
if ni < 0 or ni > 9 : print "error"
pii = p[(lng - i) mod 8 + 1][ni + 1]
c = d[c + 1][pii + 1]
if verbose = 1 : print " " & lng - i & " " & ni & " " & pii & " " & c
.
if verbose = 1 and validate = 0 : print "\ninv[" & c & "] = " & inv[c + 1]
if validate = 1 : return if c = 0
return inv[c + 1]
.
ss$[] = [ "236" "12345" "123456789012" ]
for i to 3
s$ = ss$[i]
verbose = if i <= 2
c = verhoeff s$ 0 verbose
print "\nThe check digit for '" & s$ & "' is '" & c & "'."
sc$ = s$
for j to 2
if j = 1
sc$ &= c
else
sc$ &= "9"
.
h$ = "incorrect"
if verhoeff sc$ 1 verbose = 1 : h$ = "correct"
print "\nThe validation for '" & sc$ & "' is " & h$ & "."
.
.- Output:
Check digit calculations for '236' i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 inv[2] = 3 The check digit for '236' is '3'. Validation calculations for '2363' i nᵢ p[i,nᵢ] c ------------------ 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 The validation for '2363' is correct. Validation calculations for '23639' i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 3 6 3 2 6 6 9 3 3 6 3 4 2 5 8 The validation for '23639' is incorrect. Check digit calculations for '12345' i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 inv[4] = 1 The check digit for '12345' is '1'. Validation calculations for '123451' i nᵢ p[i,nᵢ] c ------------------ 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The validation for '123451' is correct. Validation calculations for '1234519' i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 1 5 4 2 5 9 8 3 4 0 8 4 3 3 5 5 2 8 2 6 1 7 9 The validation for '1234519' is incorrect. The check digit for '123456789012' is '0'. The validation for '1234567890120' is correct. The validation for '12345678901209' is incorrect.
module verhoeff_mod
implicit none
!----------------------------------------------------------------
! Zero-based Verhoeff tables d, inv, p
!----------------------------------------------------------------
integer, parameter :: d1 = 10, d2 = 10
integer, parameter :: d(0:d1 - 1, 0:d2 - 1) = reshape([ &
! 10 rows of 10 ints each — exactly as in VB,
! listed row-major so we use ORDER=[2,1] below
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, &
1, 2, 3, 4, 0, 6, 7, 8, 9, 5, &
2, 3, 4, 0, 1, 7, 8, 9, 5, 6, &
3, 4, 0, 1, 2, 8, 9, 5, 6, 7, &
4, 0, 1, 2, 3, 9, 5, 6, 7, 8, &
5, 9, 8, 7, 6, 0, 4, 3, 2, 1, &
6, 5, 9, 8, 7, 1, 0, 4, 3, 2, &
7, 6, 5, 9, 8, 2, 1, 0, 4, 3, &
8, 7, 6, 5, 9, 3, 2, 1, 0, 4, &
9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ], shape=[d1, d2], order=[2, 1])
integer, parameter :: inv(0:d1 - 1) = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]
integer, parameter :: r = 8, c = 10
integer, parameter :: p(0:r - 1, 0:c - 1) = reshape([ &
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, &
1, 5, 7, 6, 2, 8, 3, 0, 9, 4, &
5, 8, 0, 3, 7, 9, 6, 1, 4, 2, &
8, 9, 1, 6, 0, 4, 3, 5, 2, 7, &
9, 4, 5, 3, 1, 2, 6, 8, 7, 0, &
4, 2, 8, 6, 5, 7, 3, 9, 0, 1, &
2, 7, 9, 3, 8, 0, 6, 4, 1, 5, &
7, 0, 4, 6, 9, 1, 3, 2, 5, 8 ], shape=[r, c], order=[2, 1])
contains
!----------------------------------------------------------------
! verhoeff()
! s : input digit-string
! validate : .TRUE. for check, .FALSE. to compute digit
! table : .TRUE. to dump tables, .FALSE. to skip
! returns
! if(validate) 1=>valid, 0=>invalid
! if(.not.validate) the computed check digit (0–9)
!----------------------------------------------------------------
function verhoeff(s, validate, table) result(res)
implicit none
character(len=*), intent(in) :: s
logical, intent(in) :: validate, table
integer :: res
integer :: c, lens, k, digit, pi
character(len=:), allocatable :: str
res = 0
! Append '0' when generating the check digit
if (.not.validate) then
str = trim(s) // '0'
else
str = trim(s)
end if
lens = len_trim(str)
c = 0
! Main Verhoeff loop: right-to-left over str
do k = lens, 1, -1
digit = ichar(str(k:k)) - ichar('0')
pi = p(mod(lens - k, size(p, 1)), digit)
c = d(c, pi)
if (table) then
write(*, '(I2,1X,I2,2X,I2,2X,I2)') lens - k, digit, pi, c
end if
end do
if (.not.validate) then
! computing check digit
res = inv(c)
else
! validating: success only if c==0
res = merge(1, 0, c == 0)
end if
end function verhoeff
end module verhoeff_mod
program test_verhoeff
use verhoeff_mod
implicit none
character(len=20), parameter :: inputs(3) = [character(len=20) :: '236', '12345', '123456789012' ]
logical, parameter :: showtable(3) = [ .true., .true., .false. ]
integer :: i, chk, ok
character(len=:), allocatable :: withchk, with9
do i = 1, 3
! Compute the check digit
chk = verhoeff(inputs(i), .false., showtable(i))
write(*, '(3A,I1)') 'Check digit for "', trim(inputs(i)), '" is ', chk
! Test two variants: one with the computed digit, one with '9'
withchk = trim(inputs(i)) // achar(ichar('0') + chk)
with9 = trim(inputs(i)) // '9'
ok = verhoeff(withchk, .true., showtable(i))
write(*, '(4A)') 'Validation for "', trim(withchk), '" : ', &
merge(' correct ', 'incorrect', ok == 1)
ok = verhoeff(with9, .true., showtable(i))
write(*, '(4A)') 'Validation for "', trim(with9), '" : ', &
merge(' correct ', 'incorrect', ok == 1)
write(*, *)
end do
end program test_verhoeff
- Output:
0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 Check digit for "236" is 3 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 Validation for "2363" : correct 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 Validation for "2369" : incorrect 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 Check digit for "12345" is 1 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 Validation for "123451" : correct 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 Validation for "123459" : incorrect Check digit for "123456789012" is 0 Validation for "1234567890120" : correct Validation for "1234567890129" : incorrect
Dim Shared As Integer d(9, 9) = { _
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, _
{1, 2, 3, 4, 0, 6, 7, 8, 9, 5}, _
{2, 3, 4, 0, 1, 7, 8, 9, 5, 6}, _
{3, 4, 0, 1, 2, 8, 9, 5, 6, 7}, _
{4, 0, 1, 2, 3, 9, 5, 6, 7, 8}, _
{5, 9, 8, 7, 6, 0, 4, 3, 2, 1}, _
{6, 5, 9, 8, 7, 1, 0, 4, 3, 2}, _
{7, 6, 5, 9, 8, 2, 1, 0, 4, 3}, _
{8, 7, 6, 5, 9, 3, 2, 1, 0, 4}, _
{9, 8, 7, 6, 5, 4, 3, 2, 1, 0} }
Dim Shared As Integer inv(9) = {0, 4, 3, 2, 1, 5, 6, 7, 8, 9}
Dim Shared As Integer p(7, 9) = { _
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, _
{1, 5, 7, 6, 2, 8, 3, 0, 9, 4}, _
{5, 8, 0, 3, 7, 9, 6, 1, 4, 2}, _
{8, 9, 1, 6, 0, 4, 3, 5, 2, 7}, _
{9, 4, 5, 3, 1, 2, 6, 8, 7, 0}, _
{4, 2, 8, 6, 5, 7, 3, 9, 0, 1}, _
{2, 7, 9, 3, 8, 0, 6, 4, 1, 5}, _
{7, 0, 4, 6, 9, 1, 3, 2, 5, 8} }
Function Verhoeff(s As String, validate As Integer, table As Integer) As Integer
Dim As Integer c, le, k, ni, pi
If table Then
Print
Print Iif(validate, "Validation", "Check digit") & " calculations for '" & s & "':"
Print !"\n i ni p[i,ni] c\n------------------"
End If
If Not validate Then s = s & "0"
c = 0
le = Len(s) - 1
For k = le To 0 Step -1
ni = Asc(Mid(s, k + 1, 1)) - 48
pi = p((le - k) Mod 8, ni)
c = d(c, pi)
If table Then Print Using "## # # #"; le - k; ni; pi; c
Next k
If table And Not validate Then Print !"\ninv[" & c & "] = " & inv(c)
Return Iif(Not validate, inv(c), c = 0)
End Function
Type miTipo
s As String
b As Boolean
End Type
Dim sts(2) As miTipo
sts(0).s = "236" : sts(0).b = True
sts(1).s = "12345" : sts(1).b = True
sts(2).s = "123456789012" : sts(2).b = False
Dim As Integer i, j, v , c
For i = 0 To 2
c = Verhoeff(sts(i).s, False, sts(i).b)
Print Using !"\nThe check digit for '&' is '&'"; sts(i).s; c
Dim stc(1) As String = {Left(sts(i).s, Len(sts(i).s)-1) & Str(c), Left(sts(i).s, Len(sts(i).s)-1) & "9"}
For j = 0 To Ubound(stc)
v = Verhoeff(stc(j), True, sts(i).b)
Print Using !"\nThe validation for '&' is "; stc(j);
Print Iif (v, "correct", "incorrect"); "."
Next j
Print
Next i
Sleep
- Output:
Same as Wren entry.
// Verhoeff algorithm. Nigel Galloway: August 26th., 2021
let d,inv,p=let d=[|0;1;2;3;4;5;6;7;8;9;1;2;3;4;0;6;7;8;9;5;2;3;4;0;1;7;8;9;5;6;3;4;0;1;2;8;9;5;6;7;4;0;1;2;3;9;5;6;7;8;5;9;8;7;6;0;4;3;2;1;6;5;9;8;7;1;0;4;3;2;7;6;5;9;8;2;1;0;4;3;8;7;6;5;9;3;2;1;0;4;9;8;7;6;5;4;3;2;1;0|]
let p=[|0;1;2;3;4;5;6;7;8;9;1;5;7;6;2;8;3;0;9;4;5;8;0;3;7;9;6;1;4;2;8;9;1;6;0;4;3;5;2;7;9;4;5;3;1;2;6;8;7;0;4;2;8;6;5;7;3;9;0;1;2;7;9;3;8;0;6;4;1;5;7;0;4;6;9;1;3;2;5;8|]
let inv=[|0;4;3;2;1;5;6;7;8;9|] in (fun n g->d.[10*n+g]),(fun g->inv.[g]),(fun n g->p.[10*(n%8)+g])
let fN g=Seq.unfold(fun(i,g,l)->if i=0I then None else let ni=int(i%10I) in let l=d l (p g ni) in Some((ni,l),(i/10I,g+1,l)))(g,0,0)
let csum g=let _,g=Seq.last(fN g) in inv g
let printTable g=printfn $"Work Table for %A{g}\n i nᵢ p[i,nᵢ] c\n--------------"; fN g|>Seq.iteri(fun i (n,g)->printfn $"%d{i} %d{n} %d{p i n} %d{g}")
printTable 2360I
printfn $"\nThe CheckDigit for 236 is %d{csum 2360I}\n"
printTable 2363I
printfn $"\nThe assertion that 2363 is valid is %A{csum 2363I=0}\n"
printTable 2369I
printfn $"\nThe assertion that 2369 is valid is %A{csum 2369I=0}\n"
printTable 123450I
printfn $"\nThe CheckDigit for 12345 is %d{csum 123450I}\n"
printTable 123451I
printfn $"\nThe assertion that 123451 is valid is %A{csum 123451I=0}\n"
printTable 123459I
printfn $"\nThe assertion that 123459 is valid is %A{csum 123459I=0}"
printfn $"The CheckDigit for 123456789012 is %d{csum 1234567890120I}"
printfn $"The assertion that 1234567890120 is valid is %A{csum 1234567890120I=0}"
printfn $"The assertion that 1234567890129 is valid is %A{csum 1234567890129I=0}"
- Output:
Work Table for 2360 i nᵢ p[i,nᵢ] c -------------- 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 The CheckDigit for 236 is 3 Work Table for 2363 i nᵢ p[i,nᵢ] c -------------- 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 The assertion that 2363 is valid is true Work Table for 2369 i nᵢ p[i,nᵢ] c -------------- 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 The assertion that 2369 is valid is false Work Table for 123450 i nᵢ p[i,nᵢ] c -------------- 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 The CheckDigit for 12345 is 1 Work Table for 123451 i nᵢ p[i,nᵢ] c -------------- 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The assertion that 123451 is valid is true Work Table for 123459 i nᵢ p[i,nᵢ] c -------------- 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 The assertion that 123459 is valid is false The CheckDigit for 123456789012 is 0 The assertion that 1234567890120 is valid is true The assertion that 1234567890129 is valid is false
package main
import "fmt"
var d = [][]int{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{1, 2, 3, 4, 0, 6, 7, 8, 9, 5},
{2, 3, 4, 0, 1, 7, 8, 9, 5, 6},
{3, 4, 0, 1, 2, 8, 9, 5, 6, 7},
{4, 0, 1, 2, 3, 9, 5, 6, 7, 8},
{5, 9, 8, 7, 6, 0, 4, 3, 2, 1},
{6, 5, 9, 8, 7, 1, 0, 4, 3, 2},
{7, 6, 5, 9, 8, 2, 1, 0, 4, 3},
{8, 7, 6, 5, 9, 3, 2, 1, 0, 4},
{9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
}
var inv = []int{0, 4, 3, 2, 1, 5, 6, 7, 8, 9}
var p = [][]int{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{1, 5, 7, 6, 2, 8, 3, 0, 9, 4},
{5, 8, 0, 3, 7, 9, 6, 1, 4, 2},
{8, 9, 1, 6, 0, 4, 3, 5, 2, 7},
{9, 4, 5, 3, 1, 2, 6, 8, 7, 0},
{4, 2, 8, 6, 5, 7, 3, 9, 0, 1},
{2, 7, 9, 3, 8, 0, 6, 4, 1, 5},
{7, 0, 4, 6, 9, 1, 3, 2, 5, 8},
}
func verhoeff(s string, validate, table bool) interface{} {
if table {
t := "Check digit"
if validate {
t = "Validation"
}
fmt.Printf("%s calculations for '%s':\n\n", t, s)
fmt.Println(" i nᵢ p[i,nᵢ] c")
fmt.Println("------------------")
}
if !validate {
s = s + "0"
}
c := 0
le := len(s) - 1
for i := le; i >= 0; i-- {
ni := int(s[i] - 48)
pi := p[(le-i)%8][ni]
c = d[c][pi]
if table {
fmt.Printf("%2d %d %d %d\n", le-i, ni, pi, c)
}
}
if table && !validate {
fmt.Printf("\ninv[%d] = %d\n", c, inv[c])
}
if !validate {
return inv[c]
}
return c == 0
}
func main() {
ss := []string{"236", "12345", "123456789012"}
ts := []bool{true, true, false, true}
for i, s := range ss {
c := verhoeff(s, false, ts[i]).(int)
fmt.Printf("\nThe check digit for '%s' is '%d'\n\n", s, c)
for _, sc := range []string{s + string(c+48), s + "9"} {
v := verhoeff(sc, true, ts[i]).(bool)
ans := "correct"
if !v {
ans = "incorrect"
}
fmt.Printf("\nThe validation for '%s' is %s\n\n", sc, ans)
}
}
}
- Output:
Identical to Wren example
Implementation:
cyc=: | +/~@i. NB. cyclic group, order y
ac=: |(+-/~@i.) NB. anticyclic group, order y
a2n=: (+#)@ NB. add 2^n
di=: (cyc,.cyc a2n),((ac a2n),.ac)
D=: di 5
INV=: ,I.0=D
P=: {&(C.1 5 8 9 4 2 7 0;3 6)^:(i.8) i.10
verhoeff=: {{
c=. 0
for_N. |.10 #.inv y do.
c=. D{~<c,P{~<(8|N_index),N
end.
}}
traceverhoeff=: {{
r=. EMPTY
c=. 0
for_N. |.10 #.inv y do.
c0=. c
c=. D{~<c,p=.P{~<(j=.8|N_index),N
r=. r, c,p,j,N_index,N,c0
end.
labels=. cut 'cᵢ p[i,nᵢ] i nᵢ n cₒ'
1 1}.}:~.":labels,(<;._1"1~[:*/' '=])' ',.":r
}}
checkdigit=: INV {~ verhoeff@*&10
valid=: 0 = verhoeff
Task examples:
checkdigit 236 12345 123456789012
3 1 0
valid 2363
1
valid 123451
1
valid 1234567890120
1
valid 2369
0
valid 123459
0
valid 1234567890129
0
traceverhoeff 2363
cᵢ│p[i,nᵢ]│i│nᵢ│n│cₒ│
──┼───────┼─┼──┼─┼──┤
3 │3 │0│0 │3│0 │
1 │3 │1│1 │6│3 │
4 │3 │2│2 │3│1 │
0 │1 │3│3 │2│4 │
traceverhoeff 123451
cᵢ│p[i,nᵢ]│i│nᵢ│n│cₒ│
──┼───────┼─┼──┼─┼──┤
1 │1 │0│0 │1│0 │
9 │8 │1│1 │5│1 │
2 │7 │2│2 │4│9 │
8 │6 │3│3 │3│2 │
3 │5 │4│4 │2│8 │
0 │2 │5│5 │1│3 │
import java.util.Arrays;
import java.util.List;
public class VerhoeffAlgorithm {
public static void main(String[] args) {
initialise();
List<List<Object>> tests = List.of(
List.of( "236", true ), List.of( "12345", true ), List.of( "123456789012", false ) );
for ( List<Object> test : tests ) {
Object object = verhoeffChecksum((String) test.get(0), false, (boolean) test.get(1));
System.out.println("The check digit for " + test.get(0) + " is " + object + "\n");
for ( String number : List.of( test.get(0) + String.valueOf(object), test.get(0) + "9" ) ) {
object = verhoeffChecksum(number, true, (boolean) test.get(1));
String result = (boolean) object ? "correct" : "incorrect";
System.out.println("The validation for " + number + " is " + result + ".\n");
}
}
}
private static Object verhoeffChecksum(String number, boolean doValidation, boolean doDisplay) {
if ( doDisplay ) {
String calculationType = doValidation ? "Validation" : "Check digit";
System.out.println(calculationType + " calculations for " + number + "\n");
System.out.println(" i ni p[i, ni] c");
System.out.println("-------------------");
}
if ( ! doValidation ) {
number += "0";
}
int c = 0;
final int le = number.length() - 1;
for ( int i = le; i >= 0; i-- ) {
final int ni = number.charAt(i) - '0';
final int pi = permutationTable.get((le - i) % 8).get(ni);
c = multiplicationTable.get(c).get(pi);
if ( doDisplay ) {
System.out.println(String.format("%2d%3d%8d%6d\n", le - i, ni, pi, c));
}
}
if ( doDisplay && ! doValidation ) {
System.out.println("inverse[" + c + "] = " + inverse.get(c) + "\n");
}
return doValidation ? c == 0 : inverse.get(c);
}
private static void initialise() {
multiplicationTable = List.of(
List.of( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ),
List.of( 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 ),
List.of( 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 ),
List.of( 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 ),
List.of( 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 ),
List.of( 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 ),
List.of( 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 ),
List.of( 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 ),
List.of( 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 ),
List.of( 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 )
);
inverse = Arrays.asList( 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 );
permutationTable = List.of(
List.of( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ),
List.of( 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 ),
List.of( 5, 8, 0, 3, 7, 9, 6, 1, 4, 2 ),
List.of( 8, 9, 1, 6, 0, 4, 3, 5, 2, 7 ),
List.of( 9, 4, 5, 3, 1, 2, 6, 8, 7, 0 ),
List.of( 4, 2, 8, 6, 5, 7, 3, 9, 0, 1 ),
List.of( 2, 7, 9, 3, 8, 0, 6, 4, 1, 5 ),
List.of( 7, 0, 4, 6, 9, 1, 3, 2, 5, 8 )
);
}
private static List<List<Integer>> multiplicationTable;
private static List<Integer> inverse;
private static List<List<Integer>> permutationTable;
}
- Output:
The same as the Wren example.
const multiplicationTable = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
];
const inverse = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];
const permutationTable = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
];
function verhoeffChecksum(number, doValidation, doDisplay) {
if (doDisplay) {
const calculationType = doValidation ? "Validation" : "Check digit";
console.log(calculationType + " calculations for " + number + "\n");
console.log(" i ni p[i, ni] c");
console.log("-------------------");
}
if (!doValidation) {
number += "0";
}
let c = 0;
const le = number.length - 1;
for (let i = le; i >= 0; i--) {
const ni = parseInt(number.charAt(i));
const pi = permutationTable[(le - i) % 8][ni];
c = multiplicationTable[c][pi];
if (doDisplay) {
console.log(`${String(le - i).padStart(2)}${String(ni).padStart(3)}${String(pi).padStart(8)}${String(c).padStart(6)}\n`);
}
}
if (doDisplay && !doValidation) {
console.log("inverse[" + c + "] = " + inverse[c] + "\n");
}
return doValidation ? c === 0 : inverse[c];
}
function main() {
const tests = [
["236", true],
["12345", true],
["123456789012", false],
];
for (const test of tests) {
let object = verhoeffChecksum(test[0], false, test[1]);
console.log("The check digit for " + test[0] + " is " + object + "\n");
for (const number of [test[0] + String(object), test[0] + "9"]) {
object = verhoeffChecksum(number, true, test[1]);
const result = object ? "correct" : "incorrect";
console.log("The validation for " + number + " is " + result + ".\n");
}
}
}
main();
- Output:
Check digit calculations for 236 i ni p[i, ni] c ------------------- 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 inverse[2] = 3 The check digit for 236 is 3 Validation calculations for 2363 i ni p[i, ni] c ------------------- 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 The validation for 2363 is correct. Validation calculations for 2369 i ni p[i, ni] c ------------------- 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 The validation for 2369 is incorrect. Check digit calculations for 12345 i ni p[i, ni] c ------------------- 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 inverse[4] = 1 The check digit for 12345 is 1 Validation calculations for 123451 i ni p[i, ni] c ------------------- 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The validation for 123451 is correct. Validation calculations for 123459 i ni p[i, ni] c ------------------- 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 The validation for 123459 is incorrect. The check digit for 123456789012 is 0 The validation for 1234567890120 is correct. The validation for 1234567890129 is incorrect.
Works with gojq, the Go implementation of jq
def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .;
def d: [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
];
def inv: [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];
def p: [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8]
];
# Output: an object: {emit, c}
def verhoeff($s; $validate; $table):
{emit:
(if $table then
["\(if $validate then "Validation" else "Check digit" end) calculations for '\($s)':\n",
" i nᵢ p[i,nᵢ] c",
"------------------"]
else []
end),
s: (if $validate then $s else $s + "0" end),
c: 0 }
| ((.s|length) - 1) as $le
| reduce range($le; -1; -1) as $i (.;
(.s[$i:$i+1]|explode[] - 48) as $ni
| (p[($le-$i) % 8][$ni]) as $pi
| .c = d[.c][$pi]
| if $table
then .emit += ["\($le-$i|lpad(2)) \($ni) \($pi) \(.c)"]
else .
end )
| if $table and ($validate|not)
then .emit += ["\ninv[\(.c)] = \(inv[.c])"]
else .
end
| .c = (if $validate then (.c == 0) else inv[.c] end);
def sts: [
["236", true],
["12345", true],
["123456789012", false]];
def task:
sts[]
| . as $st
| verhoeff($st[0]; false; $st[1]) as {c: $c, emit: $emit}
| $emit[],
"\nThe check digit for '\($st[0])' is '\($c)'\n",
( ($st[0] + ($c|tostring)), ($st[0] + "9")
| . as $stc
| verhoeff($stc; true; $st[1]) as {emit: $emit, c: $v}
| (if $v then "correct" else "incorrect" end) as $v
| $emit[],
"\nThe validation for '\($stc)' is \($v).\n" );
task- Output:
As for #Wren.
const multiplicationtable = [
0 1 2 3 4 5 6 7 8 9;
1 2 3 4 0 6 7 8 9 5;
2 3 4 0 1 7 8 9 5 6;
3 4 0 1 2 8 9 5 6 7;
4 0 1 2 3 9 5 6 7 8;
5 9 8 7 6 0 4 3 2 1;
6 5 9 8 7 1 0 4 3 2;
7 6 5 9 8 2 1 0 4 3;
8 7 6 5 9 3 2 1 0 4;
9 8 7 6 5 4 3 2 1 0]
const permutationtable = [
0 1 2 3 4 5 6 7 8 9;
1 5 7 6 2 8 3 0 9 4;
5 8 0 3 7 9 6 1 4 2;
8 9 1 6 0 4 3 5 2 7;
9 4 5 3 1 2 6 8 7 0;
4 2 8 6 5 7 3 9 0 1;
2 7 9 3 8 0 6 4 1 5;
7 0 4 6 9 1 3 2 5 8]
const inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]
"""
verhoeffchecksum(n::Integer, validate=true, terse=true, verbose=false)
Calculate the Verhoeff checksum over `n`.
Terse mode or with single argument: return true if valid (last digit is a correct check digit).
If checksum mode, return the expected correct checksum digit.
If validation mode, return true if last digit checks correctly.
"""
function verhoeffchecksum(n::Integer, validate=true, terse=true, verbose=false)
verbose && println("\n", validate ? "Validation" : "Check digit",
" calculations for '$n':\n\n", " i nᵢ p[i,nᵢ] c\n------------------")
# transform number list
c, dig = 0, reverse(digits(validate ? n : 10 * n))
for i in length(dig):-1:1
ni = dig[i]
p = permutationtable[(length(dig) - i) % 8 + 1, ni + 1]
c = multiplicationtable[c + 1, p + 1]
verbose && println(lpad(length(dig) - i, 2), " $ni $p $c")
end
verbose && !validate && println("\ninv($c) = $(inv[c + 1])")
!terse && println(validate ? "\nThe validation for '$n' is $(c == 0 ?
"correct" : "incorrect")." : "\nThe check digit for '$n' is $(inv[c + 1]).")
return validate ? c == 0 : inv[c + 1]
end
for args in [(236, false, false, true), (2363, true, false, true), (2369, true, false, true),
(12345, false, false, true), (123451, true, false, true), (123459, true, false, true),
(123456789012, false, false), (1234567890120, true, false), (1234567890129, true, false)]
verhoeffchecksum(args...)
end
- Output:
Same as Wren example.
import strformat
const
D = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]]
Inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]
P = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8]]
type Digit = 0..9
proc verhoeff[T: SomeInteger](n: T; validate, verbose = false): T =
## Compute or validate a check digit.
## Return the check digit if computation or the number with the check digit
## removed if validation.
## If not in verbose mode, an exception is raised if validation failed.
doAssert n >= 0, "Argument must not be negative."
# Extract digits.
var digits: seq[Digit]
if not validate: digits.add 0
var val = n
while val != 0:
digits.add val mod 10
val = val div 10
if verbose:
echo if validate: &"Check digit validation for {n}:" else: &"Check digit computation for {n}:"
echo " i ni p(i, ni) c"
# Compute c.
var c = 0
for i, ni in digits:
let p = P[i mod 8][ni]
c = D[c][p]
if verbose: echo &"{i:2} {ni} {p} {c}"
if validate:
if verbose:
let verb = if c == 0: "is" else: "is not"
echo &"Validation {verb} successful.\n"
elif c != 0:
raise newException(ValueError, &"Check digit validation failed for {n}.")
result = n div 10
else:
result = Inv[c]
if verbose: echo &"The check digit for {n} is {result}.\n"
for n in [236, 12345]:
let d = verhoeff(n, false, true)
discard verhoeff(10 * n + d, true, true)
discard verhoeff(10 * n + 9, true, true)
let n = 123456789012
let d = verhoeff(n)
echo &"Check digit for {n} is {d}."
discard verhoeff(10 * n + d, true)
echo &"Check digit validation was successful for {10 * n + d}."
try:
discard verhoeff(10 * n + 9, true)
except ValueError:
echo getCurrentExceptionMsg()
- Output:
Check digit computation for 236: i ni p(i, ni) c 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 The check digit for 236 is 3. Check digit validation for 2363: i ni p(i, ni) c 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 Validation is successful. Check digit validation for 2369: i ni p(i, ni) c 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 Validation is not successful. Check digit computation for 12345: i ni p(i, ni) c 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 The check digit for 12345 is 1. Check digit validation for 123451: i ni p(i, ni) c 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 Validation is successful. Check digit validation for 123459: i ni p(i, ni) c 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 Validation is not successful. Check digit for 123456789012 is 0. Check digit validation was successful for 1234567890120. Check digit validation failed for 1234567890129.
MODULE VerhoeffAlgorithm; (* translated from the Wren sample via FreeBASIC & Algol 68 *)
IMPORT Out, Strings;
VAR d : ARRAY 10, 10 OF INTEGER;
inv : ARRAY 10 OF INTEGER;
p : ARRAY 8, 10 OF INTEGER;
(* initialises an array of 10 INTEGERs *)
PROCEDURE set10( VAR a : ARRAY OF INTEGER
; v0, v1, v2, v3, v4, v5, v6, v7, v8, v9 : INTEGER
);
BEGIN
a[ 0 ] := v0; a[ 1 ] := v1; a[ 2 ] := v2; a[ 3 ] := v3; a[ 4 ] := v4;
a[ 5 ] := v5; a[ 6 ] := v6; a[ 7 ] := v7; a[ 8 ] := v8; a[ 9 ] := v9
END set10;
PROCEDURE init; (* initialises the tables for the Verhoeff algorithm *)
BEGIN
set10( d[ 0 ], 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 );
set10( d[ 1 ], 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 );
set10( d[ 2 ], 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 );
set10( d[ 3 ], 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 );
set10( d[ 4 ], 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 );
set10( d[ 5 ], 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 );
set10( d[ 6 ], 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 );
set10( d[ 7 ], 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 );
set10( d[ 8 ], 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 );
set10( d[ 9 ], 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 );
set10( inv, 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 );
set10( p[ 0 ], 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 );
set10( p[ 1 ], 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 );
set10( p[ 2 ], 5, 8, 0, 3, 7, 9, 6, 1, 4, 2 );
set10( p[ 3 ], 8, 9, 1, 6, 0, 4, 3, 5, 2, 7 );
set10( p[ 4 ], 9, 4, 5, 3, 1, 2, 6, 8, 7, 0 );
set10( p[ 5 ], 4, 2, 8, 6, 5, 7, 3, 9, 0, 1 );
set10( p[ 6 ], 2, 7, 9, 3, 8, 0, 6, 4, 1, 5 );
set10( p[ 7 ], 7, 0, 4, 6, 9, 1, 3, 2, 5, 8 );
END init ;
(* calculates the check digit of s or validates s using the Verhoeff algorithm *)
(* depending on whether validate is FALSE or TRUE *)
(* returns the check digit or zero/non-zero if s is valid/invalid *)
(* if table is TRUE, details of the calculations are shown *)
PROCEDURE VerhoeffAlgorithm( s : ARRAY OF CHAR; validate, table : BOOLEAN ) : INTEGER;
VAR c, le, efl, k, result : INTEGER;
PROCEDURE handleDigit( digit : CHAR; c, k, le : INTEGER; table : BOOLEAN ) : INTEGER;
VAR nidx, pidx, result : INTEGER;
BEGIN
nidx := ORD( digit ) - ORD( "0" );
pidx := p[ ( le - k ) MOD 8, nidx ];
result := d[ c, pidx ];
IF table THEN
Out.String( " " );Out.Int( le - k, 2 );Out.String( " " );Out.Int( nidx, 0 );
Out.String( " " );Out.Int( pidx, 0 );Out.String( " " );Out.Int( result, 0 );Out.Ln
END
RETURN result
END handleDigit;
BEGIN
IF table THEN
IF validate THEN Out.String( "Validation" ) ELSE Out.String( "Check digit" ) END;
Out.String( " calculations for '" );Out.String( s );Out.String( "':" );Out.Ln;
Out.String( " i ni p[i,ni] c" );Out.Ln;Out.String( " ------------------" );Out.Ln;
END;
c := 0;
le := Strings.Length( s );
IF validate THEN efl := le ELSE efl := le + 1 END;
IF ~ validate THEN
(* calculating - pretend there is an extra 0 at the end of the string *)
c := handleDigit( "0", c, efl - 1, efl - 1, table )
END;
FOR k := le - 1 TO 0 BY -1 DO
c := handleDigit( s[ k ], c, k, efl - 1, table )
END;
IF table & ~ validate THEN
Out.String( " inv[" );Out.Int( c, 0 );Out.String( "] = " );Out.Int( inv[ c ], 0 );Out.Ln
END;
IF ~ validate THEN result := inv[ c ] ELSE result := c END
RETURN result
END VerhoeffAlgorithm ;
(* returns TRUE if s is valid according to the Verhoeff algorithm, FALSE otherwise *)
(* if table is TRUE, details of the calculations are shown *)
PROCEDURE VerhoeffValidation( s : ARRAY OF CHAR; table : BOOLEAN ) : BOOLEAN;
BEGIN
RETURN VerhoeffAlgorithm( s, TRUE, table ) = 0
END VerhoeffValidation ;
(* returns the check digit of s according to the Verhoeff algorithm, FALSE otherwise *)
(* if table is TRUE, details of the calculations are shown *)
PROCEDURE VerhoeffCheckDigit( s : ARRAY OF CHAR; table : BOOLEAN ) : INTEGER;
BEGIN
RETURN VerhoeffAlgorithm( s, FALSE, table )
END VerhoeffCheckDigit ;
PROCEDURE testCases; (* run the Verhoeff algorithm task test cases *)
PROCEDURE oneTest( s : ARRAY OF CHAR; table : BOOLEAN );
VAR c : INTEGER;
PROCEDURE oneValidationTest( s : ARRAY OF CHAR; extraDigit : CHAR; table : BOOLEAN );
VAR result : ARRAY 16 OF CHAR;
sd : ARRAY 32 OF CHAR;
sLen : INTEGER;
BEGIN
sd := s;
sLen := Strings.Length( sd );
sd[ sLen ] := extraDigit;
sd[ sLen + 1 ] := 0X;
IF VerhoeffValidation( sd, table ) THEN
result := "correct"
ELSE
result := "incorrect"
END;
Out.String( "Validation for '" );Out.String( sd );Out.String( "' -> " );
Out.String( result );Out.String( "." );Out.Ln
END oneValidationTest ;
BEGIN
c := VerhoeffCheckDigit( s, table );
Out.String( "The check digit for '" );Out.String( s );
Out.String( "' is '" );Out.Int( c, 0 );Out.String( "'" );Out.Ln;
oneValidationTest( s, CHR( c + ORD( "0" ) ), table );
oneValidationTest( s, "9", table )
END oneTest ;
BEGIN
oneTest( "236", TRUE ); Out.Ln;Out.Ln;
oneTest( "12345", TRUE ); Out.Ln;Out.Ln;
oneTest( "123456789012", FALSE );
END testCases;
BEGIN
init;
testCases
END VerhoeffAlgorithm.
- Output:
Check digit calculations for '236':
i ni p[i,ni] c
------------------
0 0 0 0
1 6 3 3
2 3 3 1
3 2 1 2
inv[2] = 3
The check digit for '236' is '3'
Validation calculations for '2363':
i ni p[i,ni] c
------------------
0 3 3 3
1 6 3 1
2 3 3 4
3 2 1 0
Validation for '2363' -> correct.
Validation calculations for '2369':
i ni p[i,ni] c
------------------
0 9 9 9
1 6 3 6
2 3 3 8
3 2 1 7
Validation for '2369' -> incorrect.
Check digit calculations for '12345':
i ni p[i,ni] c
------------------
0 0 0 0
1 5 8 8
2 4 7 1
3 3 6 7
4 2 5 2
5 1 2 4
inv[4] = 1
The check digit for '12345' is '1'
Validation calculations for '123451':
i ni p[i,ni] c
------------------
0 1 1 1
1 5 8 9
2 4 7 2
3 3 6 8
4 2 5 3
5 1 2 0
Validation for '123451' -> correct.
Validation calculations for '123459':
i ni p[i,ni] c
------------------
0 9 9 9
1 5 8 1
2 4 7 8
3 3 6 2
4 2 5 7
5 1 2 5
Validation for '123459' -> incorrect.
The check digit for '123456789012' is '0'
Validation for '1234567890120' -> correct.
Validation for '1234567890129' -> incorrect.
#!/usr/bin/perl
use strict; # https://rosettacode.org/wiki/Verhoeff_algorithm
use warnings;
my @inv = qw(0 4 3 2 1 5 6 7 8 9);
my @d = map [ split ], split /\n/, <<END;
0 1 2 3 4 5 6 7 8 9
1 2 3 4 0 6 7 8 9 5
2 3 4 0 1 7 8 9 5 6
3 4 0 1 2 8 9 5 6 7
4 0 1 2 3 9 5 6 7 8
5 9 8 7 6 0 4 3 2 1
6 5 9 8 7 1 0 4 3 2
7 6 5 9 8 2 1 0 4 3
8 7 6 5 9 3 2 1 0 4
9 8 7 6 5 4 3 2 1 0
END
my @p = map [ split ], split /\n/, <<END;
0 1 2 3 4 5 6 7 8 9
1 5 7 6 2 8 3 0 9 4
5 8 0 3 7 9 6 1 4 2
8 9 1 6 0 4 3 5 2 7
9 4 5 3 1 2 6 8 7 0
4 2 8 6 5 7 3 9 0 1
2 7 9 3 8 0 6 4 1 5
7 0 4 6 9 1 3 2 5 8
END
my $debug;
sub generate
{
local $_ = shift() . 0;
my $c = my $i = 0;
my ($n, $p);
$debug and print "i ni d(c,p(i%8,ni)) c\n";
while( length )
{
$c = $d[ $c ][ $p = $p[ $i % 8 ][ $n = chop ] ];
$debug and printf "%d%3d%7d%10d\n", $i, $n, $p, $c;
$i++;
}
return $inv[ $c ];
}
sub validate { shift =~ /(\d+)(\d)/ and $2 == generate($1) }
for ( 236, 12345, 123456789012 )
{
print "testing $_\n";
$debug = length() < 6;
my $checkdigit = generate($_);
print "check digit for $_ is $checkdigit\n";
$debug = 0;
for my $cd ( $checkdigit, 9 )
{
print "$_$cd is ", validate($_ . $cd) ? '' : 'not ', "valid\n";
}
print "\n";
}
- Output:
testing 236 i ni d(c,p(i%8,ni)) c 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 check digit for 236 is 3 2363 is valid 2369 is not valid testing 12345 i ni d(c,p(i%8,ni)) c 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 check digit for 12345 is 1 123451 is valid 123459 is not valid testing 123456789012 check digit for 123456789012 is 0 1234567890120 is valid 1234567890129 is not valid
The tables were generated in case 1-based index versions of them would help, tbh, but in the end I didn't even try that, aka start with tagset(10).
with javascript_semantics
sequence d = {tagset(9,0)},
inv = tagset(9,0),
p = {tagset(9,0)}
for i=1 to 4 do d = append(d,extract(d[$],{2,3,4,5,1,7,8,9,10,6})) end for
for i=5 to 8 do d = append(d,reverse(d[-4])) end for
d = append(d,reverse(d[1]))
inv[2..5] = reverse(inv[2..5])
for i=1 to 7 do p = append(p,extract(p[$],{2,6,8,7,3,9,4,1,10,5})) end for
-- alternatively, if you prefer:
--constant d = {{0,1,2,3,4,5,6,7,8,9},
-- {1,2,3,4,0,6,7,8,9,5},
-- {2,3,4,0,1,7,8,9,5,6},
-- {3,4,0,1,2,8,9,5,6,7},
-- {4,0,1,2,3,9,5,6,7,8},
-- {5,9,8,7,6,0,4,3,2,1},
-- {6,5,9,8,7,1,0,4,3,2},
-- {7,6,5,9,8,2,1,0,4,3},
-- {8,7,6,5,9,3,2,1,0,4},
-- {9,8,7,6,5,4,3,2,1,0}},
-- inv = {0,4,3,2,1,5,6,7,8,9},
-- p = {{0,1,2,3,4,5,6,7,8,9},
-- {1,5,7,6,2,8,3,0,9,4},
-- {5,8,0,3,7,9,6,1,4,2},
-- {8,9,1,6,0,4,3,5,2,7},
-- {9,4,5,3,1,2,6,8,7,0},
-- {4,2,8,6,5,7,3,9,0,1},
-- {2,7,9,3,8,0,6,4,1,5},
-- {7,0,4,6,9,1,3,2,5,8}}
function verhoeff(string n, bool validate=false, show_workings=false)
string {s,t} = iff(validate?{n,"Validation"}:{n&'0',"Check digit"})
if show_workings then
printf(1,"%s calculations for `%s`:\n", {t, n})
printf(1," i ni p(i,ni) c\n")
printf(1,"------------------\n")
end if
integer c = 0
for i=1 to length(s) do
integer ni = s[-i]-'0',
pi = p[remainder(i-1,8)+1][ni+1]
c = d[c+1][pi+1]
if show_workings then
printf(1,"%2d %d %d %d\n", {i-1, ni, pi, c})
end if
end for
integer ch = inv[c+1]+'0'
string r = iff(validate?iff(c=0?"":"in")&"correct"
:"`"&ch&"`")
printf(1,"The %s for `%s` is %s\n\n",{lower(t),n,r})
return ch
end function
constant tests = {"236", "12345", "123456789012"}
for i=1 to length(tests) do
bool show_workings = (i<=2)
integer ch = verhoeff(tests[i],false,show_workings)
assert(verhoeff(tests[i]&ch,true,show_workings)=='0')
assert(verhoeff(tests[i]&'9',true,show_workings)!='0')
end for
- Output:
Check digit calculations for `236`: i ni p(i,ni) c ------------------ 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 The check digit for `236` is `3` Validation calculations for `2363`: i ni p(i,ni) c ------------------ 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 The validation for `2363` is correct Validation calculations for `2369`: i ni p(i,ni) c ------------------ 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 The validation for `2369` is incorrect Check digit calculations for `12345`: i ni p(i,ni) c ------------------ 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 The check digit for `12345` is `1` Validation calculations for `123451`: i ni p(i,ni) c ------------------ 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The validation for `123451` is correct Validation calculations for `123459`: i ni p(i,ni) c ------------------ 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 The validation for `123459` is incorrect The check digit for `123456789012` is `0` The validation for `1234567890120` is correct The validation for `1234567890129` is incorrect
local fmt = require "fmt"
local d = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{1, 2, 3, 4, 0, 6, 7, 8, 9, 5},
{2, 3, 4, 0, 1, 7, 8, 9, 5, 6},
{3, 4, 0, 1, 2, 8, 9, 5, 6, 7},
{4, 0, 1, 2, 3, 9, 5, 6, 7, 8},
{5, 9, 8, 7, 6, 0, 4, 3, 2, 1},
{6, 5, 9, 8, 7, 1, 0, 4, 3, 2},
{7, 6, 5, 9, 8, 2, 1, 0, 4, 3},
{8, 7, 6, 5, 9, 3, 2, 1, 0, 4},
{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
}
local inv = {0, 4, 3, 2, 1, 5, 6, 7, 8, 9}
local p = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{1, 5, 7, 6, 2, 8, 3, 0, 9, 4},
{5, 8, 0, 3, 7, 9, 6, 1, 4, 2},
{8, 9, 1, 6, 0, 4, 3, 5, 2, 7},
{9, 4, 5, 3, 1, 2, 6, 8, 7, 0},
{4, 2, 8, 6, 5, 7, 3, 9, 0, 1},
{2, 7, 9, 3, 8, 0, 6, 4, 1, 5},
{7, 0, 4, 6, 9, 1, 3, 2, 5, 8}
}
local function verhoeff(s, validate, table)
if table then
print($"{validate ? "Validation" : "Check digit"} calculations for '{s}':\n")
print(" i nᵢ p[i,nᵢ] c")
print("------------------")
end
if !validate then s ..= "0" end
local c = 0
local le = #s - 1
for i = le, 0, -1 do
local ni = s[i + 1]:byte() - 48
local pi = p[(le - i) % 8 + 1][ni + 1]
c = d[c + 1][pi + 1]
if table then fmt.print("%2d %d %d %d", le - i, ni, pi, c) end
end
if table and !validate then print($"\ninv[{c + 1}] = {inv[c + 1]}") end
return !validate ? inv[c + 1] : c == 0
end
local sts = {{"236", true}, {"12345", true}, {"123456789012", false}}
for sts as st do
local c = verhoeff(st[1], false, st[2])
print($"\nThe check digit for '{st[1]}' is '{c}'\n")
for {st[1] .. tostring(c), st[1] .. "9"} as stc do
local v = verhoeff(stc, true, st[2])
print($"\nThe validation for '{stc}' is {v ? "correct" : "incorrect"}.\n")
end
end
- Output:
Note: 1-based indexing for 'inv' array.
Check digit calculations for '236': i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 inv[3] = 3 The check digit for '236' is '3' Validation calculations for '2363': i nᵢ p[i,nᵢ] c ------------------ 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 The validation for '2363' is correct. Validation calculations for '2369': i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 The validation for '2369' is incorrect. Check digit calculations for '12345': i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 inv[5] = 1 The check digit for '12345' is '1' Validation calculations for '123451': i nᵢ p[i,nᵢ] c ------------------ 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The validation for '123451' is correct. Validation calculations for '123459': i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 The validation for '123459' is incorrect. The check digit for '123456789012' is '0' The validation for '1234567890120' is correct. The validation for '1234567890129' is incorrect.
MULTIPLICATION_TABLE = [
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(1, 2, 3, 4, 0, 6, 7, 8, 9, 5),
(2, 3, 4, 0, 1, 7, 8, 9, 5, 6),
(3, 4, 0, 1, 2, 8, 9, 5, 6, 7),
(4, 0, 1, 2, 3, 9, 5, 6, 7, 8),
(5, 9, 8, 7, 6, 0, 4, 3, 2, 1),
(6, 5, 9, 8, 7, 1, 0, 4, 3, 2),
(7, 6, 5, 9, 8, 2, 1, 0, 4, 3),
(8, 7, 6, 5, 9, 3, 2, 1, 0, 4),
(9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
]
INV = (0, 4, 3, 2, 1, 5, 6, 7, 8, 9)
PERMUTATION_TABLE = [
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(1, 5, 7, 6, 2, 8, 3, 0, 9, 4),
(5, 8, 0, 3, 7, 9, 6, 1, 4, 2),
(8, 9, 1, 6, 0, 4, 3, 5, 2, 7),
(9, 4, 5, 3, 1, 2, 6, 8, 7, 0),
(4, 2, 8, 6, 5, 7, 3, 9, 0, 1),
(2, 7, 9, 3, 8, 0, 6, 4, 1, 5),
(7, 0, 4, 6, 9, 1, 3, 2, 5, 8),
]
def verhoeffchecksum(n, validate=True, terse=True, verbose=False):
"""
Calculate the Verhoeff checksum over `n`.
Terse mode or with single argument: return True if valid (last digit is a correct check digit).
If checksum mode, return the expected correct checksum digit.
If validation mode, return True if last digit checks correctly.
"""
if verbose:
print(f"\n{'Validation' if validate else 'Check digit'}",\
f"calculations for {n}:\n\n i nᵢ p[i,nᵢ] c\n------------------")
# transform number list
c, dig = 0, list(str(n if validate else 10 * n))
for i, ni in enumerate(dig[::-1]):
p = PERMUTATION_TABLE[i % 8][int(ni)]
c = MULTIPLICATION_TABLE[c][p]
if verbose:
print(f"{i:2} {ni} {p} {c}")
if verbose and not validate:
print(f"\ninv({c}) = {INV[c]}")
if not terse:
print(f"\nThe validation for '{n}' is {'correct' if c == 0 else 'incorrect'}."\
if validate else f"\nThe check digit for '{n}' is {INV[c]}.")
return c == 0 if validate else INV[c]
if __name__ == '__main__':
for n, va, t, ve in [
(236, False, False, True), (2363, True, False, True), (2369, True, False, True),
(12345, False, False, True), (123451, True, False, True), (123459, True, False, True),
(123456789012, False, False, False), (1234567890120, True, False, False),
(1234567890129, True, False, False)]:
verhoeffchecksum(n, va, t, ve)
- Output:
Output same as Wren example.
options(scipen=999) # prevent scientific notation
multiplicationtable <- matrix(c(
0,1,2,3,4,5,6,7,8,9,
1,2,3,4,0,6,7,8,9,5,
2,3,4,0,1,7,8,9,5,6,
3,4,0,1,2,8,9,5,6,7,
4,0,1,2,3,9,5,6,7,8,
5,9,8,7,6,0,4,3,2,1,
6,5,9,8,7,1,0,4,3,2,
7,6,5,9,8,2,1,0,4,3,
8,7,6,5,9,3,2,1,0,4,
9,8,7,6,5,4,3,2,1,0
), nrow=10, byrow=TRUE)
permutationtable <- matrix(c(
0,1,2,3,4,5,6,7,8,9,
1,5,7,6,2,8,3,0,9,4,
5,8,0,3,7,9,6,1,4,2,
8,9,1,6,0,4,3,5,2,7,
9,4,5,3,1,2,6,8,7,0,
4,2,8,6,5,7,3,9,0,1,
2,7,9,3,8,0,6,4,1,5,
7,0,4,6,9,1,3,2,5,8
), nrow=8, byrow=TRUE)
inv <- c(0,4,3,2,1,5,6,7,8,9)
verhoeffchecksum <- function(n, validate=TRUE, terse=TRUE, verbose=FALSE) {
nstr <- as.character(n) # keep number as string
if (verbose) {
cat("\n", ifelse(validate, "Validation", "Check digit"),
" calculations for '", nstr, "':\n\n",
" i nᵢ p[i,nᵢ] c\n------------------\n", sep="")
}
dig <- as.integer(strsplit(if (validate) nstr else paste0(nstr, "0"), "")[[1]])
dig <- rev(dig) # Julia uses reverse
c <- 0
for (i in seq_along(dig)) {
ni <- dig[i]
p <- permutationtable[(i - 1) %% 8 + 1, ni + 1]
c <- multiplicationtable[c + 1, p + 1]
if (verbose) {
cat(sprintf("%2d %d %d %d\n", i-1, ni, p, c))
}
}
if (verbose && !validate) {
cat("\ninv[", c, "] = ", inv[c + 1], "\n", sep="")
}
if (!terse) {
if (validate) {
cat("\nThe validation for '", nstr, "' is ",
ifelse(c == 0, "correct", "incorrect"), ".\n", sep="")
} else {
cat("\nThe check digit for '", nstr, "' is '", inv[c + 1], "'\n", sep="")
}
}
if (validate) {
return(c == 0)
} else {
return(inv[c + 1])
}
}
# Test runs
tests <- list(
list("236", FALSE, FALSE, TRUE),
list("2363", TRUE, FALSE, TRUE),
list("2369", TRUE, FALSE, TRUE),
list("12345", FALSE, FALSE, TRUE),
list("123451", TRUE, FALSE, TRUE),
list("123459", TRUE, FALSE, TRUE),
list("123456789012", FALSE, FALSE),
list("1234567890120", TRUE, FALSE),
list("1234567890129", TRUE, FALSE)
)
for (args in tests) {
do.call(verhoeffchecksum, args)
}
- Output:
Check digit calculations for '236': i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 inv[2] = 3 The check digit for '236' is '3' Validation calculations for '2363': i nᵢ p[i,nᵢ] c ------------------ 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 The validation for '2363' is correct. Validation calculations for '2369': i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 The validation for '2369' is incorrect. Check digit calculations for '12345': i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 inv[4] = 1 The check digit for '12345' is '1' Validation calculations for '123451': i nᵢ p[i,nᵢ] c ------------------ 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The validation for '123451' is correct. Validation calculations for '123459': i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 The validation for '123459' is incorrect. The check digit for '123456789012' is '0' The validation for '1234567890120' is correct. The validation for '1234567890129' is incorrect.
Generate the tables rather than hard coding, They're not all that complex.
my @d = [^10] xx 5;
@d[$_][^5].=rotate($_), @d[$_][5..*].=rotate($_) for 1..4;
push @d: [@d[$_].reverse] for flat 1..4, 0;
my @i = 0,4,3,2,1,5,6,7,8,9;
my %h = flat (0,1,5,8,9,4,2,7,0).rotor(2 =>-1).map({.[0]=>.[1]}), 6=>3, 3=>6;
my @p = [^10],;
@p.push: [@p[*-1].map: {%h{$_}}] for ^7;
sub checksum (Int $int where * ≥ 0, :$verbose = True ) {
my @digits = $int.comb;
say "\nCheckdigit calculation for $int:";
say " i ni p(i, ni) c" if $verbose;
my ($i, $p, $c) = 0 xx 3;
say " $i 0 $p $c" if $verbose;
for @digits.reverse {
++$i;
$p = @p[$i % 8][$_];
$c = @d[$c; $p];
say "{$i.fmt('%2d')} $_ $p $c" if $verbose;
}
say "Checkdigit: {@i[$c]}";
+($int ~ @i[$c]);
}
sub validate (Int $int where * ≥ 0, :$verbose = True) {
my @digits = $int.comb;
say "\nValidation calculation for $int:";
say " i ni p(i, ni) c" if $verbose;
my ($i, $p, $c) = 0 xx 3;
for @digits.reverse {
$p = @p[$i % 8][$_];
$c = @d[$c; $p];
say "{$i.fmt('%2d')} $_ $p $c" if $verbose;
++$i;
}
say "Checkdigit: {'in' if $c}correct";
}
## TESTING
for 236, 12345, 123456789012 -> $int {
my $check = checksum $int, :verbose( $int.chars < 8 );
validate $check, :verbose( $int.chars < 8 );
validate +($check.chop ~ 9), :verbose( $int.chars < 8 );
}
- Output:
Checkdigit calculation for 236: i ni p(i, ni) c 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 Checkdigit: 3 Validation calculation for 2363: i ni p(i, ni) c 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 Checkdigit: correct Validation calculation for 2369: i ni p(i, ni) c 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 Checkdigit: incorrect Checkdigit calculation for 12345: i ni p(i, ni) c 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 Checkdigit: 1 Validation calculation for 123451: i ni p(i, ni) c 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 Checkdigit: correct Validation calculation for 123459: i ni p(i, ni) c 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 Checkdigit: incorrect Checkdigit calculation for 123456789012: Checkdigit: 0 Validation calculation for 1234567890120: Checkdigit: correct Validation calculation for 1234567890129: Checkdigit: incorrect
/* REXX implementation of the Verhoeff Algorithm */
Call init -- fill the tables (data taken from Java Script)
Call check 236,1 -- compute check digit AND validate numbers
Call check 12345,1 -- "
Call check 123456789012,0 -- validate numbers
Exit
check:
Parse Arg number,details
numberx=number
Say 'Check digit calculations for' number
d=compute(number||0,details,1)
Say 'The check digit for' numberx 'is' d
Call check2 numberx||d,details
Call check2 numberx||9,details
Return
check2:
Parse Arg number,details
If details Then
Say 'Validation calculations for' number
d=compute(number,details,0)
If d=0 Then
Say 'The validation for' number' is correct.'
Else
Say 'The validation for' number' is incorrect.'
Return
compute:
Parse Arg number,details,show_inv
Call details ''
Call details ' i ni p[i, ni] c'
Call details '-------------------'
c=0
le=length(number)-1
Do i=le To 0 By-1
ni=substr(number,i+1,1)
z=(le-i)//8
pi=perm.z.ni
c=mult.c.pi
Call details right(le-i,2) right(ni,2) right(pi,7) right(c,5)
End
If show_inv Then
Call details "inverse["c"] = "invt.c
Return invt.c
details:
If details Then
Say arg(1)
Return
init:
Call mk_mult '[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]',
' [1, 2, 3, 4, 0, 6, 7, 8, 9, 5]',
' [2, 3, 4, 0, 1, 7, 8, 9, 5, 6]',
' [3, 4, 0, 1, 2, 8, 9, 5, 6, 7]',
' [4, 0, 1, 2, 3, 9, 5, 6, 7, 8]',
' [5, 9, 8, 7, 6, 0, 4, 3, 2, 1]',
' [6, 5, 9, 8, 7, 1, 0, 4, 3, 2]',
' [7, 6, 5, 9, 8, 2, 1, 0, 4, 3]',
' [8, 7, 6, 5, 9, 3, 2, 1, 0, 4]',
' [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]]'
Call mk_invt '[0, 4, 3, 2, 1, 5, 6, 7, 8, 9]'
Call mk_perm '[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]',
' [1, 5, 7, 6, 2, 8, 3, 0, 9, 4]',
' [5, 8, 0, 3, 7, 9, 6, 1, 4, 2]',
' [8, 9, 1, 6, 0, 4, 3, 5, 2, 7]',
' [9, 4, 5, 3, 1, 2, 6, 8, 7, 0]',
' [4, 2, 8, 6, 5, 7, 3, 9, 0, 1]',
' [2, 7, 9, 3, 8, 0, 6, 4, 1, 5]',
' [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]]'
Return
mk_mult:
Parse Arg list
list=translate(list,' ','[],')
Do i=0 to 9
Do j=0 to 9
Parse Var list mult.i.j list
End
End
Return
mk_invt:
Parse Arg list
list=translate(list,' ','[],')
Do i=0 to 9
Parse Var list invt.i list
End
Return
mk_perm:
Parse Arg list
list=translate(list,' ','[],')
Do i=0 to 9
Do j=0 to 9
Parse Var list perm.i.j list
End
End
Return
show_mult:
If details Then Say'show_mult'
Do i=0 To 9
l=''
Do j=1 To 9
l=l mult.i.j
End
Call details l
End
return
- Output:
Check digit calculations for 236 i ni p[i, ni] c ------------------- 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 inverse[2] = 3 The check digit for 236 is 3 Validation calculations for 2363 i ni p[i, ni] c ------------------- 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 The validation for 2363 is correct. Validation calculations for 2369 i ni p[i, ni] c ------------------- 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 The validation for 2369 is incorrect. Check digit calculations for 12345 i ni p[i, ni] c ------------------- 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 inverse[4] = 1 The check digit for 12345 is 1 Validation calculations for 123451 i ni p[i, ni] c ------------------- 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The validation for 123451 is correct. Validation calculations for 123459 i ni p[i, ni] c ------------------- 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 The validation for 123459 is incorrect. Check digit calculations for 123456789012 The check digit for 123456789012 is 0 The validation for 1234567890120 is correct. The validation for 1234567890129 is incorrect.
const MULTIPLICATION_TABLE: [[i32; 10]; 10] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
];
const INVERSE: [i32; 10] = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];
const PERMUTATION_TABLE: [[i32; 10]; 8] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
];
fn verhoeff_checksum(number: &str, do_validation: bool, do_display: bool) -> i32 {
if do_display {
let calculation_type = if do_validation {
"Validation"
} else {
"Check digit"
};
println!("{} calculations for {}\n", calculation_type, number);
println!(" i ni p[i, ni] c");
println!("-------------------");
}
let mut working_number = String::from(number);
if !do_validation {
working_number.push('0');
}
let mut c = 0;
let le = working_number.len() - 1;
let chars: Vec<char> = working_number.chars().collect();
for i in (0..=le).rev() {
let ni = chars[i].to_digit(10).unwrap() as i32;
let pos = (le - i) % 8;
let pi = PERMUTATION_TABLE[pos][ni as usize];
c = MULTIPLICATION_TABLE[c as usize][pi as usize];
if do_display {
println!(
"{:2} {:2} {:2} {:2}\n",
le - i,
ni,
pi,
c
);
}
}
if do_display && !do_validation {
println!("inverse[{}] = {}\n", c, INVERSE[c as usize]);
}
if do_validation {
if c == 0 { 1 } else { 0 }
} else {
INVERSE[c as usize]
}
}
fn main() {
let tests = [
("123", true),
("12345", true),
("123456789012", false),
];
for &(test_num, display) in tests.iter() {
let digit = verhoeff_checksum(test_num, false, display);
println!("The check digit for {} is {}\n", test_num, digit);
let numbers = [
format!("{}{}", test_num, digit),
format!("{}9", test_num),
];
for number in numbers.iter() {
let digit = verhoeff_checksum(number, true, display);
let result = if digit == 1 { "correct" } else { "incorrect" };
println!("The validation for {:?} is {:?}. ", number, result);
}
}
}
- Output:
Check digit calculations for 123 i ni p[i, ni] c ------------------- 0 0 0 0 1 3 6 6 2 2 0 6 3 1 9 2 inverse[2] = 3 The check digit for 123 is 3 Validation calculations for 1233 i ni p[i, ni] c ------------------- 0 3 3 3 1 3 6 9 2 2 0 9 3 1 9 0 The validation for "1233" is "correct". Validation calculations for 1239 i ni p[i, ni] c ------------------- 0 9 9 9 1 3 6 3 2 2 0 3 3 1 9 7 The validation for "1239" is "incorrect". Check digit calculations for 12345 i ni p[i, ni] c ------------------- 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 inverse[4] = 1 The check digit for 12345 is 1 Validation calculations for 123451 i ni p[i, ni] c ------------------- 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The validation for "123451" is "correct". Validation calculations for 123459 i ni p[i, ni] c ------------------- 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 The validation for "123459" is "incorrect". The check digit for 123456789012 is 0 The validation for "1234567890120" is "correct". The validation for "1234567890129" is "incorrect".
const d = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
]
const inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]
const p = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
]
fn verhoeff(ss string, validate bool, table bool) int {
mut s:= ss
if table {
mut t := "Check digit"
if validate {
t = "Validation"
}
println("$t calculations for '$s':\n")
println(" i nᵢ p[i,nᵢ] c")
println("------------------")
}
if !validate {
s = s + "0"
}
mut c := 0
le := s.len - 1
for i := le; i >= 0; i-- {
ni := int(s[i] - 48)
pi := p[(le-i)%8][ni]
c = d[c][pi]
if table {
println("${le-i:2} $ni $pi $c")
}
}
if table && !validate {
println("\ninv[$c] = ${inv[c]}")
}
if !validate {
return inv[c]
}
return int(c == 0)
}
fn main() {
ss := ["236", "12345", "123456789012"]
ts := [true, true, false, true]
for i, s in ss {
c := verhoeff(s, false, ts[i])
println("\nThe check digit for '$s' is '$c'\n")
for sc in [s + c.str(), s + "9"] {
v := verhoeff(sc, true, ts[i])
mut ans := "correct"
if v==0 {
ans = "incorrect"
}
println("\nThe validation for '$sc' is $ans\n")
}
}
}
- Output:
Identical to Wren example
import "./fmt" for Fmt
var d = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
]
var inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]
var p = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8]
]
var verhoeff = Fn.new { |s, validate, table|
if (table) {
System.print("%(validate ? "Validation" : "Check digit") calculations for '%(s)':\n")
System.print(" i nᵢ p[i,nᵢ] c")
System.print("------------------")
}
if (!validate) s = s + "0"
var c = 0
var le = s.count - 1
for (i in le..0) {
var ni = s[i].bytes[0] - 48
var pi = p[(le-i) % 8][ni]
c = d[c][pi]
if (table) Fmt.print("$2d $d $d $d", le-i, ni, pi, c)
}
if (table && !validate) System.print("\ninv[%(c)] = %(inv[c])")
return !validate ? inv[c] : c == 0
}
var sts = [["236", true], ["12345", true], ["123456789012", false]]
for (st in sts) {
var c = verhoeff.call(st[0], false, st[1])
System.print("\nThe check digit for '%(st[0])' is '%(c)'\n")
for (stc in [st[0] + c.toString, st[0] + "9"]) {
var v = verhoeff.call(stc, true, st[1])
System.print("\nThe validation for '%(stc)' is %(v ? "correct" : "incorrect").\n")
}
}
- Output:
Check digit calculations for '236': i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 6 3 3 2 3 3 1 3 2 1 2 inv[2] = 3 The check digit for '236' is '3' Validation calculations for '2363': i nᵢ p[i,nᵢ] c ------------------ 0 3 3 3 1 6 3 1 2 3 3 4 3 2 1 0 The validation for '2363' is correct. Validation calculations for '2369': i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 6 3 6 2 3 3 8 3 2 1 7 The validation for '2369' is incorrect. Check digit calculations for '12345': i nᵢ p[i,nᵢ] c ------------------ 0 0 0 0 1 5 8 8 2 4 7 1 3 3 6 7 4 2 5 2 5 1 2 4 inv[4] = 1 The check digit for '12345' is '1' Validation calculations for '123451': i nᵢ p[i,nᵢ] c ------------------ 0 1 1 1 1 5 8 9 2 4 7 2 3 3 6 8 4 2 5 3 5 1 2 0 The validation for '123451' is correct. Validation calculations for '123459': i nᵢ p[i,nᵢ] c ------------------ 0 9 9 9 1 5 8 1 2 4 7 8 3 3 6 2 4 2 5 7 5 1 2 5 The validation for '123459' is incorrect. The check digit for '123456789012' is '0' The validation for '1234567890120' is correct. The validation for '1234567890129' is incorrect.
const std = @import("std");
const print = std.debug.print;
const MULTIPLICATION_TABLE: [10][10]i32 = [_][10]i32{
[_]i32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
[_]i32{ 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 },
[_]i32{ 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 },
[_]i32{ 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 },
[_]i32{ 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 },
[_]i32{ 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 },
[_]i32{ 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 },
[_]i32{ 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 },
[_]i32{ 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 },
[_]i32{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
};
const INVERSE: [10]i32 = [_]i32{ 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 };
const PERMUTATION_TABLE: [8][10]i32 = [_][10]i32{
[_]i32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
[_]i32{ 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 },
[_]i32{ 5, 8, 0, 3, 7, 9, 6, 1, 4, 2 },
[_]i32{ 8, 9, 1, 6, 0, 4, 3, 5, 2, 7 },
[_]i32{ 9, 4, 5, 3, 1, 2, 6, 8, 7, 0 },
[_]i32{ 4, 2, 8, 6, 5, 7, 3, 9, 0, 1 },
[_]i32{ 2, 7, 9, 3, 8, 0, 6, 4, 1, 5 },
[_]i32{ 7, 0, 4, 6, 9, 1, 3, 2, 5, 8 },
};
fn verhoeffChecksum(allocator: std.mem.Allocator, number: []const u8, do_validation: bool, do_display: bool) !i32 {
if (do_display) {
const calculation_type = if (do_validation) "Validation" else "Check digit";
print("{s} calculations for {s}\n\n", .{ calculation_type, number });
print(" i ni p[i, ni] c\n", .{});
print("-------------------\n", .{});
}
var working_number = std.ArrayList(u8).init(allocator);
defer working_number.deinit();
try working_number.appendSlice(number);
if (!do_validation) {
try working_number.append('0');
}
var c: i32 = 0;
const le = working_number.items.len - 1;
var i: usize = le + 1;
while (i > 0) {
i -= 1;
const ni = working_number.items[i] - '0';
const pos = (le - i) % 8;
const pi = PERMUTATION_TABLE[pos][ni];
c = MULTIPLICATION_TABLE[@intCast(c)][@as(usize, @intCast(pi))];
if (do_display) {
print("{:2} {:2} {:2} {:2}\n\n", .{ le - i, ni, pi, c });
}
}
if (do_display and !do_validation) {
print("inverse[{}] = {}\n\n", .{ c, INVERSE[@intCast(c)] });
}
if (do_validation) {
return if (c == 0) 1 else 0;
} else {
return INVERSE[@intCast(c)];
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const Test = struct {
test_num: []const u8,
display: bool,
};
const tests = [_]Test{
.{ .test_num = "123", .display = true },
.{ .test_num = "12345", .display = true },
.{ .test_num = "123456789012", .display = false },
};
for (tests) |my_test| {
const digit = try verhoeffChecksum(allocator, my_test.test_num, false, my_test.display);
print("The check digit for {s} is {}\n\n", .{ my_test.test_num, digit });
// Create test numbers with check digit and with '9'
var correct_number = std.ArrayList(u8).init(allocator);
defer correct_number.deinit();
try correct_number.appendSlice(my_test.test_num);
try correct_number.append('0' + @as(u8, @intCast(digit)));
var incorrect_number = std.ArrayList(u8).init(allocator);
defer incorrect_number.deinit();
try incorrect_number.appendSlice(my_test.test_num);
try incorrect_number.append('9');
const numbers = [_][]const u8{ correct_number.items, incorrect_number.items };
for (numbers) |number| {
const validation_result = try verhoeffChecksum(allocator, number, true, my_test.display);
const result = if (validation_result == 1) "correct" else "incorrect";
print("The validation for \"{s}\" is \"{s}\". \n", .{ number, result });
}
}
}
- Output:
Check digit calculations for 123 i ni p[i, ni] c ------------------- 0 0 +0 +0 1 3 +6 +6 2 2 +0 +6 3 1 +9 +2 inverse[2] = 3 The check digit for 123 is 3 Validation calculations for 1233 i ni p[i, ni] c ------------------- 0 3 +3 +3 1 3 +6 +9 2 2 +0 +9 3 1 +9 +0 The validation for "1233" is "correct". Validation calculations for 1239 i ni p[i, ni] c ------------------- 0 9 +9 +9 1 3 +6 +3 2 2 +0 +3 3 1 +9 +7 The validation for "1239" is "incorrect". Check digit calculations for 12345 i ni p[i, ni] c ------------------- 0 0 +0 +0 1 5 +8 +8 2 4 +7 +1 3 3 +6 +7 4 2 +5 +2 5 1 +2 +4 inverse[4] = 1 The check digit for 12345 is 1 Validation calculations for 123451 i ni p[i, ni] c ------------------- 0 1 +1 +1 1 5 +8 +9 2 4 +7 +2 3 3 +6 +8 4 2 +5 +3 5 1 +2 +0 The validation for "123451" is "correct". Validation calculations for 123459 i ni p[i, ni] c ------------------- 0 9 +9 +9 1 5 +8 +1 2 4 +7 +8 3 3 +6 +2 4 2 +5 +7 5 1 +2 +5 The validation for "123459" is "incorrect". The check digit for 123456789012 is 0 The validation for "1234567890120" is "correct". The validation for "1234567890129" is "incorrect".