Verhoeff algorithm: Difference between revisions
(Created new draft task, Verhoeff algorithm, and added a Wren solution.) |
(Added FreeBASIC) |
||
(34 intermediate revisions by 14 users not shown) | |||
Line 1: | Line 1: | ||
{{ |
{{task}} |
||
;Description |
;Description |
||
The [https://en.wikipedia.org/wiki/Verhoeff_algorithm 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. |
The [https://en.wikipedia.org/wiki/Verhoeff_algorithm 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. |
||
As the workings of the |
As the workings of the algorithm are clearly described in the linked Wikipedia article they will not be repeated here. |
||
;Task: |
;Task: |
||
Line 19: | Line 19: | ||
* [[Damm algorithm]] |
* [[Damm algorithm]] |
||
<br><br> |
<br><br> |
||
=={{header|11l}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="11l">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)</syntaxhighlight> |
|||
{{out}} |
|||
The same as in Python. |
|||
=={{header|C}}== |
|||
<syntaxhighlight lang="c">#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; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
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. |
|||
</pre> |
|||
=={{header|C++}}== |
|||
<syntaxhighlight lang="c++"> |
|||
#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; |
|||
} |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{ out }} |
|||
<pre> |
|||
The same as the Wren example. |
|||
</pre> |
|||
=={{header|FreeBASIC}}== |
|||
{{trans|Wren}} |
|||
<syntaxhighlight lang="vbnet">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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Same as Wren entry.</pre> |
|||
=={{header|F_Sharp|F#}}== |
|||
<syntaxhighlight lang="fsharp"> |
|||
// 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}" |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
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 |
|||
</pre> |
|||
=={{header|Go}}== |
|||
{{trans|Wren}} |
|||
<syntaxhighlight lang="go">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) |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Identical to Wren example |
|||
</pre> |
|||
=={{header|J}}== |
|||
Implementation: |
|||
<syntaxhighlight lang="j">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</syntaxhighlight> |
|||
Task examples: |
|||
<syntaxhighlight lang="j"> 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 │ |
|||
</syntaxhighlight> |
|||
=={{header|Java}}== |
|||
<syntaxhighlight lang="java"> |
|||
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; |
|||
} |
|||
</syntaxhighlight> |
|||
{{ out }} |
|||
<pre> |
|||
The same as the Wren example. |
|||
</pre> |
|||
=={{header|jq}}== |
|||
{{trans|Wren}} |
|||
{{works with|jq}} |
|||
'''Works with gojq, the Go implementation of jq''' |
|||
<syntaxhighlight lang="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</syntaxhighlight> |
|||
{{out}} |
|||
As for [[#Wren]]. |
|||
=={{header|Julia}}== |
|||
<syntaxhighlight lang="julia">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 |
|||
</syntaxhighlight>{{out}}Same as Wren example. |
|||
=={{header|Nim}}== |
|||
<syntaxhighlight lang="nim">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()</syntaxhighlight> |
|||
{{out}} |
|||
<pre>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.</pre> |
|||
=={{header|Perl}}== |
|||
<syntaxhighlight lang="perl">#!/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"; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
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 |
|||
</pre> |
|||
=={{header|Phix}}== |
|||
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). |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #7060A8;">tagset</span><span style="color: #0000FF;">(</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)},</span> |
|||
<span style="color: #000000;">inv</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">tagset</span><span style="color: #0000FF;">(</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">p</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #7060A8;">tagset</span><span style="color: #0000FF;">(</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)}</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">4</span> <span style="color: #008080;">do</span> <span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">extract</span><span style="color: #0000FF;">(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">[$],{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">10</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">}))</span> <span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">5</span> <span style="color: #008080;">to</span> <span style="color: #000000;">8</span> <span style="color: #008080;">do</span> <span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">reverse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">[-</span><span style="color: #000000;">4</span><span style="color: #0000FF;">]))</span> <span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">reverse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]))</span> |
|||
<span style="color: #000000;">inv</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">..</span><span style="color: #000000;">5</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">reverse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">inv</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">..</span><span style="color: #000000;">5</span><span style="color: #0000FF;">])</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">7</span> <span style="color: #008080;">do</span> <span style="color: #000000;">p</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">p</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">extract</span><span style="color: #0000FF;">(</span><span style="color: #000000;">p</span><span style="color: #0000FF;">[$],{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">10</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">}))</span> <span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #000080;font-style:italic;">-- 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}}</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">verhoeff</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">bool</span> <span style="color: #000000;">validate</span><span style="color: #0000FF;">=</span><span style="color: #004600;">false</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">show_workings</span><span style="color: #0000FF;">=</span><span style="color: #004600;">false</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #000000;">t</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">validate</span><span style="color: #0000FF;">?{</span><span style="color: #000000;">n</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Validation"</span><span style="color: #0000FF;">}:{</span><span style="color: #000000;">n</span><span style="color: #0000FF;">&</span><span style="color: #008000;">'0'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Check digit"</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">show_workings</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s calculations for `%s`:\n"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">t</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" i ni p(i,ni) c\n"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"------------------\n"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">ni</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[-</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]-</span><span style="color: #008000;">'0'</span><span style="color: #0000FF;">,</span> |
|||
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">p</span><span style="color: #0000FF;">[</span><span style="color: #7060A8;">remainder</span><span style="color: #0000FF;">(</span><span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">][</span><span style="color: #000000;">ni</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">d</span><span style="color: #0000FF;">[</span><span style="color: #000000;">c</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">][</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">show_workings</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%2d %d %d %d\n"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ni</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pi</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">inv</span><span style="color: #0000FF;">[</span><span style="color: #000000;">c</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]+</span><span style="color: #008000;">'0'</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">r</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">validate</span><span style="color: #0000FF;">?</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span><span style="color: #0000FF;">?</span><span style="color: #008000;">""</span><span style="color: #0000FF;">:</span><span style="color: #008000;">"in"</span><span style="color: #0000FF;">)&</span><span style="color: #008000;">"correct"</span> |
|||
<span style="color: #0000FF;">:</span><span style="color: #008000;">"`"</span><span style="color: #0000FF;">&</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">&</span><span style="color: #008000;">"`"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"The %s for `%s` is %s\n\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">lower</span><span style="color: #0000FF;">(</span><span style="color: #000000;">t</span><span style="color: #0000FF;">),</span><span style="color: #000000;">n</span><span style="color: #0000FF;">,</span><span style="color: #000000;">r</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">ch</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">tests</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"236"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"12345"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"123456789012"</span><span style="color: #0000FF;">}</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">bool</span> <span style="color: #000000;">show_workings</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">i</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">verhoeff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #004600;">false</span><span style="color: #0000FF;">,</span><span style="color: #000000;">show_workings</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">assert</span><span style="color: #0000FF;">(</span><span style="color: #000000;">verhoeff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]&</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">,</span><span style="color: #004600;">true</span><span style="color: #0000FF;">,</span><span style="color: #000000;">show_workings</span><span style="color: #0000FF;">)==</span><span style="color: #008000;">'0'</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">assert</span><span style="color: #0000FF;">(</span><span style="color: #000000;">verhoeff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]&</span><span style="color: #008000;">'9'</span><span style="color: #0000FF;">,</span><span style="color: #004600;">true</span><span style="color: #0000FF;">,</span><span style="color: #000000;">show_workings</span><span style="color: #0000FF;">)!=</span><span style="color: #008000;">'0'</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
|||
<pre> |
|||
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 |
|||
</pre> |
|||
=={{header|Python}}== |
|||
<syntaxhighlight lang="python">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) |
|||
</syntaxhighlight>{{out}}Output same as Wren example. |
|||
=={{header|Raku}}== |
|||
Generate the tables rather than hard coding, They're not all that complex. |
|||
<syntaxhighlight lang="raku" line>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 ); |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>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</pre> |
|||
=={{header|V (Vlang)}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="go">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") |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Identical to Wren example |
|||
</pre> |
|||
=={{header|Wren}}== |
=={{header|Wren}}== |
||
{{libheader|Wren-fmt}} |
{{libheader|Wren-fmt}} |
||
< |
<syntaxhighlight lang="wren">import "./fmt" for Fmt |
||
var d = [ |
var d = [ |
||
Line 76: | Line 1,566: | ||
System.print("\nThe validation for '%(stc)' is %(v ? "correct" : "incorrect").\n") |
System.print("\nThe validation for '%(stc)' is %(v ? "correct" : "incorrect").\n") |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
Latest revision as of 20:47, 26 March 2024
You are encouraged to solve this task according to the task description, using any language you may know.
- Description
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.
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
11l
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.
C
#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.
C++
#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.
FreeBASIC
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.
F#
// 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
Go
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
J
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 │
Java
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.
jq
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.
Julia
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.
Nim
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.
Perl
#!/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
Phix
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
Python
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.
Raku
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
V (Vlang)
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
Wren
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.