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.

Task
Verhoeff algorithm
You are encouraged to solve this task according to the task description, using any language you may know.
Description

As the workings of the algorithm are clearly described in the linked Wikipedia article they will not be repeated here.

Task

Write routines, methods, procedures etc. in your language to generate a Verhoeff checksum digit for non-negative integers of any length and to validate the result. A combined routine is also acceptable.

The more mathematically minded may prefer to generate the 3 tables required from the description provided rather than to hard-code them.

Write your routines in such a way that they can optionally display digit by digit calculations as in the Wikipedia example.

Use your routines to calculate check digits for the integers: 236, 12345 and 123456789012 and then validate them. Also attempt to validate the same integers if the check digits in all cases were 9 rather than what they actually are.

Display digit by digit calculations for the first two integers but not for the third.

Related task



Translation of: Python
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.

Translation of: FreeBASIC
Translation of: Wren

Algol 68 doesn't allow INTs to be used as BOOLs, so this has separate verhoeff validation and verhoeff check digit procedures. They both call a common verhoeff algorithm proceedure.

BEGIN # The Verhoeff algorithm - translated from the Wren sample via FreeBASIC #

    [,]INT d   = ( ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )
                 , ( 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 )
                 , ( 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 )
                 , ( 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 )
                 , ( 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 )
                 , ( 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 )
                 , ( 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 )
                 , ( 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 )
                 , ( 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 )
                 , ( 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 )
                 );
    [ ]INT inv = ( 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 );
    [,]INT p   = ( ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )
                 , ( 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 )
                 , ( 5, 8, 0, 3, 7, 9, 6, 1, 4, 2 )
                 , ( 8, 9, 1, 6, 0, 4, 3, 5, 2, 7 )
                 , ( 9, 4, 5, 3, 1, 2, 6, 8, 7, 0 )
                 , ( 4, 2, 8, 6, 5, 7, 3, 9, 0, 1 )
                 , ( 2, 7, 9, 3, 8, 0, 6, 4, 1, 5 )
                 , ( 7, 0, 4, 6, 9, 1, 3, 2, 5, 8 )
                 );
    PROC verhoeff algorithm = ( STRING s in, BOOL validate, table )INT:
         BEGIN
            IF table THEN
                print( ( IF validate THEN "Validation" ELSE "Check digit" FI ) );
                print( ( " calculations for '", s in, "':", newline ) );
                print( ( "     i  ni  p[i,ni]  c", newline, "    ------------------", newline ) )
            FI;
            STRING s = IF validate THEN s in ELSE s in + "0" FI;
            INT c := 0;
            INT le = UPB s;
            FOR k FROM le BY -1 TO LWB s DO
                INT nidx = ABS s[ k ] - 48;
                INT pidx = p[ 1 + ( le - k ) MOD 8, 1 + nidx ];
                c := d[ c + 1, pidx + 1 ];
                IF table
                THEN print( ( "    ", whole( le - k, -2 ), "  ", whole( nidx, 0 ), "      ", whole( pidx, 0 ) ) );
                     print( ( "     ", whole( c, 0 ), newline ) )
                FI
            OD;
            IF table AND NOT validate
            THEN print( ( "    inv[", whole( c, 0 ), "] = ", whole( inv[ c + 1 ], 0 ), newline ) )
            FI;
            IF NOT validate THEN inv[ c + 1 ] ELSE c FI
         END # verhoeff algorithm # ;
    PROC verhoeff validation  = ( STRING s, BOOL table )BOOL: verhoeff algorithm( s, TRUE,  table ) = 0;
    PROC verhoeff check digit = ( STRING s, BOOL table )INT:  verhoeff algorithm( s, FALSE, table );

    BEGIN # test cases                                                       #
        MODE TCASE = STRUCT( STRING s, BOOL table );
        []TCASE sts = ( ( "236", TRUE ), ( "12345", TRUE ), ( "123456789012", FALSE ) );
        FOR i FROM LWB sts TO UPB sts DO
            STRING s = s OF sts[ i ];
            INT c = verhoeff check digit( s, table OF sts[ i ] );
            print( ( "The check digit for '", s, "' is '", whole( c, 0 ), "'", newline ) );
            []STRING stc = ( s + REPR ( c + ABS "0" ), s + "9" );
            FOR j FROM LWB stc TO UPB stc DO
                BOOL v = verhoeff validation( stc[ j ], table OF sts[ i ] );
                print( ( "Validation for '", stc[ j ], "' -> " ) );
                print( ( IF v THEN "correct" ELSE "incorrect" FI, ".", newline ) )
            OD;
            print( ( newline, newline ) )
        OD
    END
END
Output:
Check digit calculations for '236':
     i  ni  p[i,ni]  c
    ------------------
     0  0      0     0
     1  6      3     3
     2  3      3     1
     3  2      1     2
    inv[2] = 3
The check digit for '236' is '3'
Validation calculations for '2363':
     i  ni  p[i,ni]  c
    ------------------
     0  3      3     3
     1  6      3     1
     2  3      3     4
     3  2      1     0
Validation for '2363' -> correct.
Validation calculations for '2369':
     i  ni  p[i,ni]  c
    ------------------
     0  9      9     9
     1  6      3     6
     2  3      3     8
     3  2      1     7
Validation for '2369' -> incorrect.


Check digit calculations for '12345':
     i  ni  p[i,ni]  c
    ------------------
     0  0      0     0
     1  5      8     8
     2  4      7     1
     3  3      6     7
     4  2      5     2
     5  1      2     4
    inv[4] = 1
The check digit for '12345' is '1'
Validation calculations for '123451':
     i  ni  p[i,ni]  c
    ------------------
     0  1      1     1
     1  5      8     9
     2  4      7     2
     3  3      6     8
     4  2      5     3
     5  1      2     0
Validation for '123451' -> correct.
Validation calculations for '123459':
     i  ni  p[i,ni]  c
    ------------------
     0  9      9     9
     1  5      8     1
     2  4      7     8
     3  3      6     2
     4  2      5     7
     5  1      2     5
Validation for '123459' -> incorrect.


The check digit for '123456789012' is '0'
Validation for '1234567890120' -> correct.
Validation for '1234567890129' -> incorrect.

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.
#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.
Translation of: C
d[][] = [ [ 0 1 2 3 4 5 6 7 8 9 ] [ 1 2 3 4 0 6 7 8 9 5 ] [ 2 3 4 0 1 7 8 9 5 6 ] [ 3 4 0 1 2 8 9 5 6 7 ] [ 4 0 1 2 3 9 5 6 7 8 ] [ 5 9 8 7 6 0 4 3 2 1 ] [ 6 5 9 8 7 1 0 4 3 2 ] [ 7 6 5 9 8 2 1 0 4 3 ] [ 8 7 6 5 9 3 2 1 0 4 ] [ 9 8 7 6 5 4 3 2 1 0 ] ]
inv[] = [ 0 4 3 2 1 5 6 7 8 9 ]
p[][] = [ [ 0 1 2 3 4 5 6 7 8 9 ] [ 1 5 7 6 2 8 3 0 9 4 ] [ 5 8 0 3 7 9 6 1 4 2 ] [ 8 9 1 6 0 4 3 5 2 7 ] [ 9 4 5 3 1 2 6 8 7 0 ] [ 4 2 8 6 5 7 3 9 0 1 ] [ 2 7 9 3 8 0 6 4 1 5 ] [ 7 0 4 6 9 1 3 2 5 8 ] ]
#
func verhoeff s$ validate verbose .
   if verbose = 1
      t$ = "Check digit"
      if validate = 1 : t$ = "Validation"
      print t$ & " calculations for '" & s$ & "'\n"
      print " i  nᵢ  p[i,nᵢ]  c"
      print "------------------"
   .
   s$[] = strchars s$
   lng = len s$[]
   if validate = 1 : lng -= 1
   for i = lng downto 0
      ni = 0
      if i < lng or validate = 1 : ni = strcode s$[i + 1] - 48
      if ni < 0 or ni > 9 : print "error"
      pii = p[(lng - i) mod 8 + 1][ni + 1]
      c = d[c + 1][pii + 1]
      if verbose = 1 : print " " & lng - i & "  " & ni & "      " & pii & "     " & c
   .
   if verbose = 1 and validate = 0 : print "\ninv[" & c & "] = " & inv[c + 1]
   if validate = 1 : return if c = 0
   return inv[c + 1]
.
ss$[] = [ "236" "12345" "123456789012" ]
for i to 3
   s$ = ss$[i]
   verbose = if i <= 2
   c = verhoeff s$ 0 verbose
   print "\nThe check digit for '" & s$ & "' is '" & c & "'."
   sc$ = s$
   for j to 2
      if j = 1
         sc$ &= c
      else
         sc$ &= "9"
      .
      h$ = "incorrect"
      if verhoeff sc$ 1 verbose = 1 : h$ = "correct"
      print "\nThe validation for '" & sc$ & "' is " & h$ & "."
   .
.
Output:
Check digit calculations for '236'

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  0      0     0
 1  6      3     3
 2  3      3     1
 3  2      1     2

inv[2] = 3

The check digit for '236' is '3'.
Validation calculations for '2363'

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  3      3     3
 1  6      3     1
 2  3      3     4
 3  2      1     0

The validation for '2363' is correct.
Validation calculations for '23639'

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  9      9     9
 1  3      6     3
 2  6      6     9
 3  3      6     3
 4  2      5     8

The validation for '23639' is incorrect.
Check digit calculations for '12345'

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  0      0     0
 1  5      8     8
 2  4      7     1
 3  3      6     7
 4  2      5     2
 5  1      2     4

inv[4] = 1

The check digit for '12345' is '1'.
Validation calculations for '123451'

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  1      1     1
 1  5      8     9
 2  4      7     2
 3  3      6     8
 4  2      5     3
 5  1      2     0

The validation for '123451' is correct.
Validation calculations for '1234519'

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  9      9     9
 1  1      5     4
 2  5      9     8
 3  4      0     8
 4  3      3     5
 5  2      8     2
 6  1      7     9

The validation for '1234519' is incorrect.

The check digit for '123456789012' is '0'.

The validation for '1234567890120' is correct.

The validation for '12345678901209' is incorrect.
module verhoeff_mod
   implicit none

   !----------------------------------------------------------------
   ! Zero-based Verhoeff tables d, inv, p
   !----------------------------------------------------------------
   integer, parameter :: d1 = 10, d2 = 10
   integer, parameter :: d(0:d1 - 1, 0:d2 - 1) = reshape([ &
   ! 10 rows of 10 ints each — exactly as in VB,
   ! listed row-major so we use ORDER=[2,1] below
         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, &
         1, 2, 3, 4, 0, 6, 7, 8, 9, 5, &
         2, 3, 4, 0, 1, 7, 8, 9, 5, 6, &
         3, 4, 0, 1, 2, 8, 9, 5, 6, 7, &
         4, 0, 1, 2, 3, 9, 5, 6, 7, 8, &
         5, 9, 8, 7, 6, 0, 4, 3, 2, 1, &
         6, 5, 9, 8, 7, 1, 0, 4, 3, 2, &
         7, 6, 5, 9, 8, 2, 1, 0, 4, 3, &
         8, 7, 6, 5, 9, 3, 2, 1, 0, 4, &
         9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ], shape=[d1, d2], order=[2, 1])

   integer, parameter :: inv(0:d1 - 1) = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]

   integer, parameter :: r = 8, c = 10
   integer, parameter :: p(0:r - 1, 0:c - 1) = reshape([ &
         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, &
         1, 5, 7, 6, 2, 8, 3, 0, 9, 4, &
         5, 8, 0, 3, 7, 9, 6, 1, 4, 2, &
         8, 9, 1, 6, 0, 4, 3, 5, 2, 7, &
         9, 4, 5, 3, 1, 2, 6, 8, 7, 0, &
         4, 2, 8, 6, 5, 7, 3, 9, 0, 1, &
         2, 7, 9, 3, 8, 0, 6, 4, 1, 5, &
         7, 0, 4, 6, 9, 1, 3, 2, 5, 8 ], shape=[r, c], order=[2, 1])

contains

   !----------------------------------------------------------------
   !  verhoeff()
   !    s        : input digit-string
   !    validate : .TRUE. for check, .FALSE. to compute digit
   !    table    : .TRUE. to dump tables,  .FALSE. to skip
   !  returns
   !    if(validate)  1=>valid, 0=>invalid
   !    if(.not.validate) the computed check digit (0–9)
   !----------------------------------------------------------------
   function verhoeff(s, validate, table) result(res)
      implicit none
      character(len=*), intent(in) :: s
      logical, intent(in) :: validate, table
      integer :: res

      integer :: c, lens, k, digit, pi
      character(len=:), allocatable :: str
      res = 0

      ! Append '0' when generating the check digit
      if (.not.validate) then
         str = trim(s) // '0'
      else
         str = trim(s)
      end if
      lens = len_trim(str)
      c = 0

      ! Main Verhoeff loop: right-to-left over str
      do k = lens, 1, -1
         digit = ichar(str(k:k)) - ichar('0')
         pi = p(mod(lens - k, size(p, 1)), digit)
         c = d(c, pi)
         if (table) then
            write(*, '(I2,1X,I2,2X,I2,2X,I2)') lens - k, digit, pi, c
         end if
      end do

      if (.not.validate) then
         ! computing check digit
         res = inv(c)
      else
         ! validating: success only if c==0
         res = merge(1, 0, c == 0)
      end if

   end function verhoeff

end module verhoeff_mod

program test_verhoeff
   use verhoeff_mod
   implicit none

   character(len=20), parameter :: inputs(3) = [character(len=20) :: '236', '12345', '123456789012' ]
   logical, parameter :: showtable(3) = [ .true., .true., .false. ]

   integer :: i, chk, ok
   character(len=:), allocatable :: withchk, with9

   do i = 1, 3
      ! Compute the check digit
      chk = verhoeff(inputs(i), .false., showtable(i))
      write(*, '(3A,I1)') 'Check digit for "', trim(inputs(i)), '" is ', chk

      ! Test two variants: one with the computed digit, one with '9'
      withchk = trim(inputs(i)) // achar(ichar('0') + chk)
      with9 = trim(inputs(i)) // '9'

      ok = verhoeff(withchk, .true., showtable(i))
      write(*, '(4A)') 'Validation for "', trim(withchk), '" : ', &
            merge(' correct ', 'incorrect', ok == 1)

      ok = verhoeff(with9, .true., showtable(i))
      write(*, '(4A)') 'Validation for "', trim(with9), '" : ', &
            merge(' correct ', 'incorrect', ok == 1)

      write(*, *)
   end do

end program test_verhoeff
Output:
0  0   0   0
 1  6   3   3
 2  3   3   1
 3  2   1   2
Check digit for "236" is 3
 0  3   3   3
 1  6   3   1
 2  3   3   4
 3  2   1   0
Validation for "2363" :  correct 
 0  9   9   9
 1  6   3   6
 2  3   3   8
 3  2   1   7
Validation for "2369" : incorrect

 0  0   0   0
 1  5   8   8
 2  4   7   1
 3  3   6   7
 4  2   5   2
 5  1   2   4
Check digit for "12345" is 1
 0  1   1   1
 1  5   8   9
 2  4   7   2
 3  3   6   8
 4  2   5   3
 5  1   2   0
Validation for "123451" :  correct 
 0  9   9   9
 1  5   8   1
 2  4   7   8
 3  3   6   2
 4  2   5   7
 5  1   2   5
Validation for "123459" : incorrect

Check digit for "123456789012" is 0
Validation for "1234567890120" :  correct 
Validation for "1234567890129" : incorrect

Translation of: Wren
Dim Shared As Integer d(9, 9) = { _
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, _
{1, 2, 3, 4, 0, 6, 7, 8, 9, 5}, _
{2, 3, 4, 0, 1, 7, 8, 9, 5, 6}, _
{3, 4, 0, 1, 2, 8, 9, 5, 6, 7}, _
{4, 0, 1, 2, 3, 9, 5, 6, 7, 8}, _
{5, 9, 8, 7, 6, 0, 4, 3, 2, 1}, _
{6, 5, 9, 8, 7, 1, 0, 4, 3, 2}, _
{7, 6, 5, 9, 8, 2, 1, 0, 4, 3}, _
{8, 7, 6, 5, 9, 3, 2, 1, 0, 4}, _
{9, 8, 7, 6, 5, 4, 3, 2, 1, 0} }

Dim Shared As Integer inv(9) = {0, 4, 3, 2, 1, 5, 6, 7, 8, 9}

Dim Shared As Integer p(7, 9) = { _
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, _
{1, 5, 7, 6, 2, 8, 3, 0, 9, 4}, _
{5, 8, 0, 3, 7, 9, 6, 1, 4, 2}, _
{8, 9, 1, 6, 0, 4, 3, 5, 2, 7}, _
{9, 4, 5, 3, 1, 2, 6, 8, 7, 0}, _
{4, 2, 8, 6, 5, 7, 3, 9, 0, 1}, _
{2, 7, 9, 3, 8, 0, 6, 4, 1, 5}, _
{7, 0, 4, 6, 9, 1, 3, 2, 5, 8} }

Function Verhoeff(s As String, validate As Integer, table As Integer) As Integer
    Dim As Integer c, le, k, ni, pi
    If table Then
        Print
        Print Iif(validate, "Validation", "Check digit") & " calculations for '" & s & "':"
        Print !"\n i  ni  p[i,ni]  c\n------------------"
    End If
    If Not validate Then s = s & "0"
    c = 0
    le = Len(s) - 1
    For k = le To 0 Step -1
        ni = Asc(Mid(s, k + 1, 1)) - 48
        pi = p((le - k) Mod 8, ni)
        c = d(c, pi)
        If table Then Print Using "##  #      #     #"; le - k; ni; pi; c
    Next k
    If table And Not validate Then Print !"\ninv[" & c & "] = " & inv(c)
    Return Iif(Not validate, inv(c), c = 0)
End Function

Type miTipo
    s As String
    b As Boolean
End Type
Dim sts(2) As miTipo
sts(0).s = "236"          : sts(0).b = True
sts(1).s = "12345"        : sts(1).b = True
sts(2).s = "123456789012" : sts(2).b = False

Dim As Integer i, j, v , c
For i = 0 To 2
    c = Verhoeff(sts(i).s, False, sts(i).b)
    Print Using !"\nThe check digit for '&' is '&'"; sts(i).s; c
    Dim stc(1) As String = {Left(sts(i).s, Len(sts(i).s)-1) & Str(c), Left(sts(i).s, Len(sts(i).s)-1) & "9"}
    For j = 0 To Ubound(stc)
        v = Verhoeff(stc(j), True, sts(i).b)
        Print Using !"\nThe validation for '&' is "; stc(j);
        Print Iif (v, "correct", "incorrect"); "."
    Next j
    Print
Next i

Sleep
Output:
Same as Wren entry.
// Verhoeff algorithm. Nigel Galloway: August 26th., 2021
let d,inv,p=let d=[|0;1;2;3;4;5;6;7;8;9;1;2;3;4;0;6;7;8;9;5;2;3;4;0;1;7;8;9;5;6;3;4;0;1;2;8;9;5;6;7;4;0;1;2;3;9;5;6;7;8;5;9;8;7;6;0;4;3;2;1;6;5;9;8;7;1;0;4;3;2;7;6;5;9;8;2;1;0;4;3;8;7;6;5;9;3;2;1;0;4;9;8;7;6;5;4;3;2;1;0|]
            let p=[|0;1;2;3;4;5;6;7;8;9;1;5;7;6;2;8;3;0;9;4;5;8;0;3;7;9;6;1;4;2;8;9;1;6;0;4;3;5;2;7;9;4;5;3;1;2;6;8;7;0;4;2;8;6;5;7;3;9;0;1;2;7;9;3;8;0;6;4;1;5;7;0;4;6;9;1;3;2;5;8|]
            let inv=[|0;4;3;2;1;5;6;7;8;9|] in (fun n g->d.[10*n+g]),(fun g->inv.[g]),(fun n g->p.[10*(n%8)+g])
let fN g=Seq.unfold(fun(i,g,l)->if i=0I then None else let ni=int(i%10I) in let l=d l (p g ni) in Some((ni,l),(i/10I,g+1,l)))(g,0,0)
let csum g=let _,g=Seq.last(fN g) in inv g
let printTable g=printfn $"Work Table for %A{g}\n i  nᵢ  p[i,nᵢ] c\n--------------"; fN g|>Seq.iteri(fun i (n,g)->printfn $"%d{i}  %d{n}      %d{p i n}    %d{g}")
printTable 2360I
printfn $"\nThe CheckDigit for 236 is %d{csum 2360I}\n"
printTable 2363I
printfn $"\nThe assertion that 2363 is valid is %A{csum 2363I=0}\n"
printTable 2369I
printfn $"\nThe assertion that 2369 is valid is %A{csum 2369I=0}\n"
printTable 123450I
printfn $"\nThe CheckDigit for 12345 is %d{csum 123450I}\n"
printTable 123451I
printfn $"\nThe assertion that 123451 is valid is %A{csum 123451I=0}\n"
printTable 123459I
printfn $"\nThe assertion that 123459 is valid is %A{csum 123459I=0}"
printfn $"The CheckDigit for 123456789012 is %d{csum 1234567890120I}"
printfn $"The assertion that 1234567890120 is valid is %A{csum 1234567890120I=0}"
printfn $"The assertion that 1234567890129 is valid is %A{csum 1234567890129I=0}"
Output:
Work Table for 2360
 i  nᵢ  p[i,nᵢ] c
--------------
0  0      0    0
1  6      3    3
2  3      3    1
3  2      1    2

The CheckDigit for 236 is 3

Work Table for 2363
 i  nᵢ  p[i,nᵢ] c
--------------
0  3      3    3
1  6      3    1
2  3      3    4
3  2      1    0

The assertion that 2363 is valid is true

Work Table for 2369
 i  nᵢ  p[i,nᵢ] c
--------------
0  9      9    9
1  6      3    6
2  3      3    8
3  2      1    7

The assertion that 2369 is valid is false

Work Table for 123450
 i  nᵢ  p[i,nᵢ] c
--------------
0  0      0    0
1  5      8    8
2  4      7    1
3  3      6    7
4  2      5    2
5  1      2    4

The CheckDigit for 12345 is 1

Work Table for 123451
 i  nᵢ  p[i,nᵢ] c
--------------
0  1      1    1
1  5      8    9
2  4      7    2
3  3      6    8
4  2      5    3
5  1      2    0

The assertion that 123451 is valid is true

Work Table for 123459
 i  nᵢ  p[i,nᵢ] c
--------------
0  9      9    9
1  5      8    1
2  4      7    8
3  3      6    2
4  2      5    7
5  1      2    5

The assertion that 123459 is valid is false
The CheckDigit for 123456789012 is 0
The assertion that 1234567890120 is valid is true
The assertion that 1234567890129 is valid is false
Translation of: Wren
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ᵢ]inᵢncₒ
──┼───────┼─┼──┼─┼──┤        
3 3      00 30         
1 3      11 63         
4 3      22 31         
0 1      33 24         
   traceverhoeff 123451
cᵢp[i,nᵢ]inᵢncₒ
──┼───────┼─┼──┼─┼──┤        
1 1      00 10         
9 8      11 51         
2 7      22 49         
8 6      33 32         
3 5      44 28         
0 2      55 13 
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.


Translation of: Java
const multiplicationTable = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
  [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
  [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
  [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
  [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
  [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
  [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
  [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
  [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
];

const inverse = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];

const permutationTable = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
  [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
  [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
  [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
  [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
  [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
  [7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
];


function verhoeffChecksum(number, doValidation, doDisplay) {
  if (doDisplay) {
    const calculationType = doValidation ? "Validation" : "Check digit";
    console.log(calculationType + " calculations for " + number + "\n");
    console.log(" i  ni  p[i, ni]  c");
    console.log("-------------------");
  }

  if (!doValidation) {
    number += "0";
  }

  let c = 0;
  const le = number.length - 1;

  for (let i = le; i >= 0; i--) {
    const ni = parseInt(number.charAt(i));
    const pi = permutationTable[(le - i) % 8][ni];
    c = multiplicationTable[c][pi];

    if (doDisplay) {
      console.log(`${String(le - i).padStart(2)}${String(ni).padStart(3)}${String(pi).padStart(8)}${String(c).padStart(6)}\n`);
    }
  }

  if (doDisplay && !doValidation) {
    console.log("inverse[" + c + "] = " + inverse[c] + "\n");
  }

  return doValidation ? c === 0 : inverse[c];
}


function main() {
  const tests = [
    ["236", true],
    ["12345", true],
    ["123456789012", false],
  ];

  for (const test of tests) {
    let object = verhoeffChecksum(test[0], false, test[1]);
    console.log("The check digit for " + test[0] + " is " + object + "\n");

    for (const number of [test[0] + String(object), test[0] + "9"]) {
      object = verhoeffChecksum(number, true, test[1]);
      const result = object ? "correct" : "incorrect";
      console.log("The validation for " + number + " is " + result + ".\n");
    }
  }
}

main();
Output:
Check digit calculations for 236

 i  ni  p[i, ni]  c
-------------------
 0  0       0     0

 1  6       3     3

 2  3       3     1

 3  2       1     2

inverse[2] = 3

The check digit for 236 is 3

Validation calculations for 2363

 i  ni  p[i, ni]  c
-------------------
 0  3       3     3

 1  6       3     1

 2  3       3     4

 3  2       1     0

The validation for 2363 is correct.

Validation calculations for 2369

 i  ni  p[i, ni]  c
-------------------
 0  9       9     9

 1  6       3     6

 2  3       3     8

 3  2       1     7

The validation for 2369 is incorrect.

Check digit calculations for 12345

 i  ni  p[i, ni]  c
-------------------
 0  0       0     0

 1  5       8     8

 2  4       7     1

 3  3       6     7

 4  2       5     2

 5  1       2     4

inverse[4] = 1

The check digit for 12345 is 1

Validation calculations for 123451

 i  ni  p[i, ni]  c
-------------------
 0  1       1     1

 1  5       8     9

 2  4       7     2

 3  3       6     8

 4  2       5     3

 5  1       2     0

The validation for 123451 is correct.

Validation calculations for 123459

 i  ni  p[i, ni]  c
-------------------
 0  9       9     9

 1  5       8     1

 2  4       7     8

 3  3       6     2

 4  2       5     7

 5  1       2     5

The validation for 123459 is incorrect.

The check digit for 123456789012 is 0

The validation for 1234567890120 is correct.

The validation for 1234567890129 is incorrect.


Translation of: Wren
Works with: 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.

const multiplicationtable = [
    0  1  2  3  4  5  6  7  8  9;
    1  2  3  4  0  6  7  8  9  5;
    2  3  4  0  1  7  8  9  5  6;
    3  4  0  1  2  8  9  5  6  7;
    4  0  1  2  3  9  5  6  7  8;
    5  9  8  7  6  0  4  3  2  1;
    6  5  9  8  7  1  0  4  3  2;
    7  6  5  9  8  2  1  0  4  3;
    8  7  6  5  9  3  2  1  0  4;
    9  8  7  6  5  4  3  2  1  0]

const permutationtable = [
    0  1  2  3  4  5  6  7  8  9;
    1  5  7  6  2  8  3  0  9  4;
    5  8  0  3  7  9  6  1  4  2;
    8  9  1  6  0  4  3  5  2  7;
    9  4  5  3  1  2  6  8  7  0;
    4  2  8  6  5  7  3  9  0  1;
    2  7  9  3  8  0  6  4  1  5;
    7  0  4  6  9  1  3  2  5  8]

const inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]

"""
    verhoeffchecksum(n::Integer, validate=true, terse=true, verbose=false)

Calculate the Verhoeff checksum over `n`.
Terse mode or with single argument: return true if valid (last digit is a correct check digit).
If checksum mode, return the expected correct checksum digit.
If validation mode, return true if last digit checks correctly.
"""
function verhoeffchecksum(n::Integer, validate=true, terse=true, verbose=false)
    verbose && println("\n", validate ? "Validation" : "Check digit",
        " calculations for '$n':\n\n", " i  nᵢ  p[i,nᵢ]  c\n------------------")
    # transform number list
    c, dig = 0, reverse(digits(validate ? n : 10 * n))
    for i in length(dig):-1:1
        ni = dig[i]
        p = permutationtable[(length(dig) - i) % 8 + 1, ni + 1]
        c = multiplicationtable[c + 1, p + 1]
        verbose && println(lpad(length(dig) - i, 2), "  $ni      $p    $c")
    end
    verbose && !validate && println("\ninv($c) = $(inv[c + 1])")
    !terse && println(validate ? "\nThe validation for '$n' is $(c == 0 ?
        "correct" : "incorrect")." : "\nThe check digit for '$n' is $(inv[c + 1]).")
    return validate ? c == 0 : inv[c + 1]
end

for args in [(236, false, false, true), (2363, true, false, true), (2369, true, false, true),
    (12345, false, false, true), (123451, true, false, true), (123459, true, false, true),
    (123456789012, false, false), (1234567890120, true, false), (1234567890129, true, false)]
        verhoeffchecksum(args...)
end
Output:

Same as Wren example.

import strformat

const

  D = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
       [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
       [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
       [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
       [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
       [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
       [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
       [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
       [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
       [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]]

  Inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]

  P = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
       [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
       [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
       [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
       [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
       [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
       [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
       [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]]

type Digit = 0..9

proc verhoeff[T: SomeInteger](n: T; validate, verbose = false): T =
  ## Compute or validate a check digit.
  ## Return the check digit if computation or the number with the check digit
  ## removed if validation.
  ## If not in verbose mode, an exception is raised if validation failed.

  doAssert n >= 0, "Argument must not be negative."

  # Extract digits.
  var digits: seq[Digit]
  if not validate: digits.add 0
  var val = n
  while val != 0:
    digits.add val mod 10
    val = val div 10

  if verbose:
    echo if validate: &"Check digit validation for {n}:" else: &"Check digit computation for {n}:"
    echo " i  ni  p(i, ni)  c"

  # Compute c.
  var c = 0
  for i, ni in digits:
    let p = P[i mod 8][ni]
    c = D[c][p]
    if verbose: echo &"{i:2}   {ni}     {p}      {c}"

  if validate:
    if verbose:
      let verb = if c == 0: "is" else: "is not"
      echo &"Validation {verb} successful.\n"
    elif c != 0:
      raise newException(ValueError, &"Check digit validation failed for {n}.")
    result = n div 10

  else:
    result = Inv[c]
    if verbose: echo &"The check digit for {n} is {result}.\n"


for n in [236, 12345]:
  let d = verhoeff(n, false, true)
  discard verhoeff(10 * n + d, true, true)
  discard verhoeff(10 * n + 9, true, true)

let n = 123456789012
let d = verhoeff(n)
echo &"Check digit for {n} is {d}."
discard verhoeff(10 * n + d, true)
echo &"Check digit validation was successful for {10 * n + d}."
try:
  discard verhoeff(10 * n + 9, true)
except ValueError:
  echo getCurrentExceptionMsg()
Output:
Check digit computation for 236:
 i  ni  p(i, ni)  c
 0   0     0      0
 1   6     3      3
 2   3     3      1
 3   2     1      2
The check digit for 236 is 3.

Check digit validation for 2363:
 i  ni  p(i, ni)  c
 0   3     3      3
 1   6     3      1
 2   3     3      4
 3   2     1      0
Validation is successful.

Check digit validation for 2369:
 i  ni  p(i, ni)  c
 0   9     9      9
 1   6     3      6
 2   3     3      8
 3   2     1      7
Validation is not successful.

Check digit computation for 12345:
 i  ni  p(i, ni)  c
 0   0     0      0
 1   5     8      8
 2   4     7      1
 3   3     6      7
 4   2     5      2
 5   1     2      4
The check digit for 12345 is 1.

Check digit validation for 123451:
 i  ni  p(i, ni)  c
 0   1     1      1
 1   5     8      9
 2   4     7      2
 3   3     6      8
 4   2     5      3
 5   1     2      0
Validation is successful.

Check digit validation for 123459:
 i  ni  p(i, ni)  c
 0   9     9      9
 1   5     8      1
 2   4     7      8
 3   3     6      2
 4   2     5      7
 5   1     2      5
Validation is not successful.

Check digit for 123456789012 is 0.
Check digit validation was successful for 1234567890120.
Check digit validation failed for 1234567890129.
Translation of: Wren – via FreeBASIC and ALGOL 68
MODULE VerhoeffAlgorithm; (* translated from the Wren sample via FreeBASIC & Algol 68    *)
    IMPORT Out, Strings;

    VAR    d              : ARRAY 10, 10 OF INTEGER;
           inv            : ARRAY     10 OF INTEGER;
           p              : ARRAY  8, 10 OF INTEGER;

    (* initialises an array of 10 INTEGERs                                               *)
    PROCEDURE set10( VAR a                                  : ARRAY OF INTEGER
                   ; v0, v1, v2, v3, v4, v5, v6, v7, v8, v9 : INTEGER
                   );
        BEGIN
            a[ 0 ] := v0; a[ 1 ] := v1; a[ 2 ] := v2; a[ 3 ] := v3; a[ 4 ] := v4;
            a[ 5 ] := v5; a[ 6 ] := v6; a[ 7 ] := v7; a[ 8 ] := v8; a[ 9 ] := v9
        END set10;

    PROCEDURE init;     (* initialises the tables for the Verhoeff algorithm             *)
        BEGIN
            set10( d[ 0 ], 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 );
            set10( d[ 1 ], 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 );
            set10( d[ 2 ], 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 );
            set10( d[ 3 ], 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 );
            set10( d[ 4 ], 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 );
            set10( d[ 5 ], 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 );
            set10( d[ 6 ], 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 );
            set10( d[ 7 ], 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 );
            set10( d[ 8 ], 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 );
            set10( d[ 9 ], 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 );

            set10( inv,    0, 4, 3, 2, 1, 5, 6, 7, 8, 9 );

            set10( p[ 0 ], 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 );
            set10( p[ 1 ], 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 );
            set10( p[ 2 ], 5, 8, 0, 3, 7, 9, 6, 1, 4, 2 );
            set10( p[ 3 ], 8, 9, 1, 6, 0, 4, 3, 5, 2, 7 );
            set10( p[ 4 ], 9, 4, 5, 3, 1, 2, 6, 8, 7, 0 );
            set10( p[ 5 ], 4, 2, 8, 6, 5, 7, 3, 9, 0, 1 );
            set10( p[ 6 ], 2, 7, 9, 3, 8, 0, 6, 4, 1, 5 );
            set10( p[ 7 ], 7, 0, 4, 6, 9, 1, 3, 2, 5, 8 );
        END init ;

    (* calculates the check digit of s or validates s using the Verhoeff algorithm       *)
    (*            depending on whether validate is FALSE or TRUE                         *)
    (* returns the check digit or zero/non-zero if s is valid/invalid                    *)
    (* if table is TRUE, details of the calculations are shown                           *)
    PROCEDURE VerhoeffAlgorithm( s : ARRAY OF CHAR; validate, table : BOOLEAN ) : INTEGER;
        VAR   c, le, efl, k, result      : INTEGER;

        PROCEDURE handleDigit( digit : CHAR; c, k, le : INTEGER; table : BOOLEAN ) : INTEGER;
            VAR   nidx, pidx, result : INTEGER;
            BEGIN
                nidx   := ORD( digit ) - ORD( "0" );
                pidx   := p[ ( le - k ) MOD 8, nidx ];
                result := d[ c, pidx ];
                IF table THEN
                    Out.String( "    " );Out.Int( le - k, 2 );Out.String( "  " );Out.Int( nidx, 0 );
                    Out.String( "      " );Out.Int( pidx, 0 );Out.String( "     " );Out.Int( result, 0 );Out.Ln
                END
            RETURN result
            END handleDigit;
      
        BEGIN
            IF table THEN
                IF validate THEN Out.String( "Validation" ) ELSE Out.String( "Check digit" ) END;
                Out.String( " calculations for '" );Out.String( s );Out.String( "':" );Out.Ln;
                Out.String( "     i  ni  p[i,ni]  c" );Out.Ln;Out.String( "    ------------------" );Out.Ln;
            END;
            c  := 0;
            le := Strings.Length( s );
            IF validate THEN efl := le ELSE efl := le + 1 END;
            IF ~ validate THEN
               (* calculating - pretend there is an extra 0 at the end of the string     *)
               c := handleDigit( "0", c, efl - 1, efl - 1, table )
            END;
            FOR k := le - 1 TO 0 BY -1 DO
                c := handleDigit( s[ k ], c, k, efl - 1, table )
            END;
            IF table & ~ validate THEN
                Out.String( "    inv[" );Out.Int( c, 0 );Out.String( "] = " );Out.Int( inv[ c ], 0 );Out.Ln
            END;
            IF ~ validate THEN result := inv[ c ] ELSE result := c END
         RETURN result
         END VerhoeffAlgorithm ;

    (* returns TRUE if s is valid according to the Verhoeff algorithm, FALSE otherwise   *)
    (* if table is TRUE, details of the calculations are shown                           *)
    PROCEDURE VerhoeffValidation( s : ARRAY OF CHAR; table : BOOLEAN ) : BOOLEAN;
        BEGIN
        RETURN VerhoeffAlgorithm( s, TRUE, table ) = 0
        END VerhoeffValidation ;

    (* returns the check digit of s according to the Verhoeff algorithm, FALSE otherwise *)
    (* if table is TRUE, details of the calculations are shown                           *)
    PROCEDURE VerhoeffCheckDigit( s : ARRAY OF CHAR; table : BOOLEAN ) : INTEGER;
        BEGIN
        RETURN VerhoeffAlgorithm( s, FALSE, table )
        END VerhoeffCheckDigit ;

    PROCEDURE testCases; (* run the Verhoeff algorithm task test cases                   *)

        PROCEDURE oneTest( s : ARRAY OF CHAR; table : BOOLEAN );
            VAR   c          : INTEGER;

            PROCEDURE oneValidationTest( s : ARRAY OF CHAR; extraDigit : CHAR; table : BOOLEAN );
                VAR   result    : ARRAY 16 OF CHAR;
                      sd        : ARRAY 32 OF CHAR;
                      sLen      : INTEGER;
                BEGIN
                    sd             := s;
                    sLen           := Strings.Length( sd );
                    sd[ sLen     ] := extraDigit;
                    sd[ sLen + 1 ] := 0X;
                    IF VerhoeffValidation( sd, table ) THEN
                        result := "correct"
                    ELSE
                        result := "incorrect"
                    END;
                    Out.String( "Validation for '" );Out.String( sd );Out.String( "' -> " );
                    Out.String( result );Out.String( "." );Out.Ln
                END oneValidationTest ;

            BEGIN
                c := VerhoeffCheckDigit( s, table );
                Out.String( "The check digit for '" );Out.String( s );
                Out.String( "' is '" );Out.Int( c, 0 );Out.String( "'" );Out.Ln;
                oneValidationTest( s, CHR( c + ORD( "0" ) ), table );
                oneValidationTest( s, "9", table )
            END oneTest ;

        BEGIN
            oneTest( "236",           TRUE );    Out.Ln;Out.Ln;
            oneTest( "12345",         TRUE );    Out.Ln;Out.Ln;
            oneTest( "123456789012", FALSE );
        END testCases;

BEGIN
    init;
    testCases
END VerhoeffAlgorithm.
Output:
Check digit calculations for '236':
     i  ni  p[i,ni]  c
    ------------------
     0  0      0     0
     1  6      3     3
     2  3      3     1
     3  2      1     2
    inv[2] = 3
The check digit for '236' is '3'
Validation calculations for '2363':
     i  ni  p[i,ni]  c
    ------------------
     0  3      3     3
     1  6      3     1
     2  3      3     4
     3  2      1     0
Validation for '2363' -> correct.
Validation calculations for '2369':
     i  ni  p[i,ni]  c
    ------------------
     0  9      9     9
     1  6      3     6
     2  3      3     8
     3  2      1     7
Validation for '2369' -> incorrect.


Check digit calculations for '12345':
     i  ni  p[i,ni]  c
    ------------------
     0  0      0     0
     1  5      8     8
     2  4      7     1
     3  3      6     7
     4  2      5     2
     5  1      2     4
    inv[4] = 1
The check digit for '12345' is '1'
Validation calculations for '123451':
     i  ni  p[i,ni]  c
    ------------------
     0  1      1     1
     1  5      8     9
     2  4      7     2
     3  3      6     8
     4  2      5     3
     5  1      2     0
Validation for '123451' -> correct.
Validation calculations for '123459':
     i  ni  p[i,ni]  c
    ------------------
     0  9      9     9
     1  5      8     1
     2  4      7     8
     3  3      6     2
     4  2      5     7
     5  1      2     5
Validation for '123459' -> incorrect.


The check digit for '123456789012' is '0'
Validation for '1234567890120' -> correct.
Validation for '1234567890129' -> incorrect.
#!/usr/bin/perl

use strict; # https://rosettacode.org/wiki/Verhoeff_algorithm
use warnings;

my @inv = qw(0 4 3 2 1 5 6 7 8 9);

my @d = map [ split ], split /\n/, <<END;
0 	1 	2 	3 	4 	5 	6 	7 	8 	9
1 	2 	3 	4 	0 	6 	7 	8 	9 	5
2 	3 	4 	0 	1 	7 	8 	9 	5 	6
3 	4 	0 	1 	2 	8 	9 	5 	6 	7
4 	0 	1 	2 	3 	9 	5 	6 	7 	8
5 	9 	8 	7 	6 	0 	4 	3 	2 	1
6 	5 	9 	8 	7 	1 	0 	4 	3 	2
7 	6 	5 	9 	8 	2 	1 	0 	4 	3
8 	7 	6 	5 	9 	3 	2 	1 	0 	4
9 	8 	7 	6 	5 	4 	3 	2 	1 	0
END

my @p = map [ split ], split /\n/, <<END;
0 	1 	2 	3 	4 	5 	6 	7 	8 	9
1 	5 	7 	6 	2 	8 	3 	0 	9 	4
5 	8 	0 	3 	7 	9 	6 	1 	4 	2
8 	9 	1 	6 	0 	4 	3 	5 	2 	7
9 	4 	5 	3 	1 	2 	6 	8 	7 	0
4 	2 	8 	6 	5 	7 	3 	9 	0 	1
2 	7 	9 	3 	8 	0 	6 	4 	1 	5
7 	0 	4 	6 	9 	1 	3 	2 	5 	8
END

my $debug;

sub generate
  {
  local $_ = shift() . 0;
  my $c = my $i = 0;
  my ($n, $p);
  $debug and print "i ni d(c,p(i%8,ni)) c\n";
  while( length )
    {
    $c = $d[ $c ][ $p = $p[ $i % 8 ][ $n = chop ] ];
    $debug and printf "%d%3d%7d%10d\n", $i, $n, $p, $c;
    $i++;
    }
  return $inv[ $c ];
  }

sub validate { shift =~ /(\d+)(\d)/ and $2 == generate($1) }

for ( 236, 12345, 123456789012 )
  {
  print "testing $_\n";
  $debug = length() < 6;
  my $checkdigit = generate($_);
  print "check digit for $_ is $checkdigit\n";
  $debug = 0;
  for my $cd ( $checkdigit, 9 )
    {
    print "$_$cd is ", validate($_ . $cd) ? '' : 'not ', "valid\n";
    }
  print "\n";
  }
Output:
testing 236
i ni d(c,p(i%8,ni)) c
0  0      0         0
1  6      3         3
2  3      3         1
3  2      1         2
check digit for 236 is 3
2363 is valid
2369 is not valid

testing 12345
i ni d(c,p(i%8,ni)) c
0  0      0         0
1  5      8         8
2  4      7         1
3  3      6         7
4  2      5         2
5  1      2         4
check digit for 12345 is 1
123451 is valid
123459 is not valid

testing 123456789012
check digit for 123456789012 is 0
1234567890120 is valid
1234567890129 is not valid

The tables were generated in case 1-based index versions of them would help, tbh, but in the end I didn't even try that, aka start with tagset(10).

with javascript_semantics
sequence d = {tagset(9,0)},
         inv = tagset(9,0),
         p = {tagset(9,0)}
for i=1 to 4 do d = append(d,extract(d[$],{2,3,4,5,1,7,8,9,10,6})) end for
for i=5 to 8 do d = append(d,reverse(d[-4])) end for
                d = append(d,reverse(d[1]))
inv[2..5] = reverse(inv[2..5])
for i=1 to 7 do p = append(p,extract(p[$],{2,6,8,7,3,9,4,1,10,5})) end for

-- alternatively, if you prefer:
--constant d = {{0,1,2,3,4,5,6,7,8,9},
--              {1,2,3,4,0,6,7,8,9,5},
--              {2,3,4,0,1,7,8,9,5,6},
--              {3,4,0,1,2,8,9,5,6,7},
--              {4,0,1,2,3,9,5,6,7,8},
--              {5,9,8,7,6,0,4,3,2,1},
--              {6,5,9,8,7,1,0,4,3,2},
--              {7,6,5,9,8,2,1,0,4,3},
--              {8,7,6,5,9,3,2,1,0,4},
--              {9,8,7,6,5,4,3,2,1,0}},
--        inv = {0,4,3,2,1,5,6,7,8,9},
--         p = {{0,1,2,3,4,5,6,7,8,9},
--              {1,5,7,6,2,8,3,0,9,4},
--              {5,8,0,3,7,9,6,1,4,2},
--              {8,9,1,6,0,4,3,5,2,7},
--              {9,4,5,3,1,2,6,8,7,0},
--              {4,2,8,6,5,7,3,9,0,1},
--              {2,7,9,3,8,0,6,4,1,5},
--              {7,0,4,6,9,1,3,2,5,8}}

function verhoeff(string n, bool validate=false, show_workings=false)
    string {s,t} = iff(validate?{n,"Validation"}:{n&'0',"Check digit"})
    if show_workings then
        printf(1,"%s calculations for `%s`:\n", {t, n})
        printf(1," i  ni  p(i,ni)  c\n")
        printf(1,"------------------\n")
    end if
    integer c = 0
    for i=1 to length(s) do
        integer ni = s[-i]-'0',
                pi = p[remainder(i-1,8)+1][ni+1]
        c = d[c+1][pi+1]
        if show_workings then
            printf(1,"%2d  %d      %d     %d\n", {i-1, ni, pi, c})
        end if
    end for
    integer ch = inv[c+1]+'0'
    string r = iff(validate?iff(c=0?"":"in")&"correct"
                           :"`"&ch&"`")
    printf(1,"The %s for `%s` is %s\n\n",{lower(t),n,r})
    return ch
end function

constant tests = {"236", "12345", "123456789012"}
for i=1 to length(tests) do
    bool show_workings = (i<=2)
    integer ch = verhoeff(tests[i],false,show_workings)
    assert(verhoeff(tests[i]&ch,true,show_workings)=='0')
    assert(verhoeff(tests[i]&'9',true,show_workings)!='0')
end for
Output:
Check digit calculations for `236`:
 i  ni  p(i,ni)  c
------------------
 0  0      0     0
 1  6      3     3
 2  3      3     1
 3  2      1     2
The check digit for `236` is `3`

Validation calculations for `2363`:
 i  ni  p(i,ni)  c
------------------
 0  3      3     3
 1  6      3     1
 2  3      3     4
 3  2      1     0
The validation for `2363` is correct

Validation calculations for `2369`:
 i  ni  p(i,ni)  c
------------------
 0  9      9     9
 1  6      3     6
 2  3      3     8
 3  2      1     7
The validation for `2369` is incorrect

Check digit calculations for `12345`:
 i  ni  p(i,ni)  c
------------------
 0  0      0     0
 1  5      8     8
 2  4      7     1
 3  3      6     7
 4  2      5     2
 5  1      2     4
The check digit for `12345` is `1`

Validation calculations for `123451`:
 i  ni  p(i,ni)  c
------------------
 0  1      1     1
 1  5      8     9
 2  4      7     2
 3  3      6     8
 4  2      5     3
 5  1      2     0
The validation for `123451` is correct

Validation calculations for `123459`:
 i  ni  p(i,ni)  c
------------------
 0  9      9     9
 1  5      8     1
 2  4      7     8
 3  3      6     2
 4  2      5     7
 5  1      2     5
The validation for `123459` is incorrect

The check digit for `123456789012` is `0`

The validation for `1234567890120` is correct

The validation for `1234567890129` is incorrect
Translation of: Wren
Library: Pluto-fmt
local fmt = require "fmt"

local d = {
    {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
    {1, 2, 3, 4, 0, 6, 7, 8, 9, 5},
    {2, 3, 4, 0, 1, 7, 8, 9, 5, 6},
    {3, 4, 0, 1, 2, 8, 9, 5, 6, 7},
    {4, 0, 1, 2, 3, 9, 5, 6, 7, 8},
    {5, 9, 8, 7, 6, 0, 4, 3, 2, 1},
    {6, 5, 9, 8, 7, 1, 0, 4, 3, 2},
    {7, 6, 5, 9, 8, 2, 1, 0, 4, 3},
    {8, 7, 6, 5, 9, 3, 2, 1, 0, 4},
    {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
}

local inv = {0, 4, 3, 2, 1, 5, 6, 7, 8, 9}

local p = {
    {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
    {1, 5, 7, 6, 2, 8, 3, 0, 9, 4},
    {5, 8, 0, 3, 7, 9, 6, 1, 4, 2},
    {8, 9, 1, 6, 0, 4, 3, 5, 2, 7},
    {9, 4, 5, 3, 1, 2, 6, 8, 7, 0},
    {4, 2, 8, 6, 5, 7, 3, 9, 0, 1},
    {2, 7, 9, 3, 8, 0, 6, 4, 1, 5},
    {7, 0, 4, 6, 9, 1, 3, 2, 5, 8}
}

local function verhoeff(s, validate, table)
    if table then
        print($"{validate ? "Validation" : "Check digit"} calculations for '{s}':\n")
        print(" i  nᵢ  p[i,nᵢ]  c")
        print("------------------")
    end
    if !validate then s ..= "0" end
    local c = 0
    local le = #s - 1
    for i = le, 0, -1 do
        local ni = s[i + 1]:byte() - 48
        local pi = p[(le - i) % 8 + 1][ni + 1]
        c = d[c + 1][pi + 1]
        if table then fmt.print("%2d  %d      %d     %d", le - i, ni, pi, c) end    
    end
    if table and !validate then print($"\ninv[{c + 1}] = {inv[c + 1]}") end
    return !validate ? inv[c + 1] : c == 0
end

local sts = {{"236", true}, {"12345", true}, {"123456789012", false}}
for sts as st do
    local c = verhoeff(st[1], false, st[2])
    print($"\nThe check digit for '{st[1]}' is '{c}'\n")
    for {st[1] .. tostring(c),  st[1] .. "9"} as stc do
        local v = verhoeff(stc, true, st[2])
        print($"\nThe validation for '{stc}' is {v ? "correct" : "incorrect"}.\n")
    end
end
Output:

Note: 1-based indexing for 'inv' array.

Check digit calculations for '236':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  0      0     0
 1  6      3     3
 2  3      3     1
 3  2      1     2

inv[3] = 3

The check digit for '236' is '3'

Validation calculations for '2363':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  3      3     3
 1  6      3     1
 2  3      3     4
 3  2      1     0

The validation for '2363' is correct.

Validation calculations for '2369':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  9      9     9
 1  6      3     6
 2  3      3     8
 3  2      1     7

The validation for '2369' is incorrect.

Check digit calculations for '12345':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  0      0     0
 1  5      8     8
 2  4      7     1
 3  3      6     7
 4  2      5     2
 5  1      2     4

inv[5] = 1

The check digit for '12345' is '1'

Validation calculations for '123451':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  1      1     1
 1  5      8     9
 2  4      7     2
 3  3      6     8
 4  2      5     3
 5  1      2     0

The validation for '123451' is correct.

Validation calculations for '123459':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  9      9     9
 1  5      8     1
 2  4      7     8
 3  3      6     2
 4  2      5     7
 5  1      2     5

The validation for '123459' is incorrect.


The check digit for '123456789012' is '0'


The validation for '1234567890120' is correct.


The validation for '1234567890129' is incorrect.
MULTIPLICATION_TABLE = [
    (0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
    (1, 2, 3, 4, 0, 6, 7, 8, 9, 5),
    (2, 3, 4, 0, 1, 7, 8, 9, 5, 6),
    (3, 4, 0, 1, 2, 8, 9, 5, 6, 7),
    (4, 0, 1, 2, 3, 9, 5, 6, 7, 8),
    (5, 9, 8, 7, 6, 0, 4, 3, 2, 1),
    (6, 5, 9, 8, 7, 1, 0, 4, 3, 2),
    (7, 6, 5, 9, 8, 2, 1, 0, 4, 3),
    (8, 7, 6, 5, 9, 3, 2, 1, 0, 4),
    (9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
]

INV = (0, 4, 3, 2, 1, 5, 6, 7, 8, 9)

PERMUTATION_TABLE = [
    (0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
    (1, 5, 7, 6, 2, 8, 3, 0, 9, 4),
    (5, 8, 0, 3, 7, 9, 6, 1, 4, 2),
    (8, 9, 1, 6, 0, 4, 3, 5, 2, 7),
    (9, 4, 5, 3, 1, 2, 6, 8, 7, 0),
    (4, 2, 8, 6, 5, 7, 3, 9, 0, 1),
    (2, 7, 9, 3, 8, 0, 6, 4, 1, 5),
    (7, 0, 4, 6, 9, 1, 3, 2, 5, 8),
]

def verhoeffchecksum(n, validate=True, terse=True, verbose=False):
    """
    Calculate the Verhoeff checksum over `n`.
    Terse mode or with single argument: return True if valid (last digit is a correct check digit).
    If checksum mode, return the expected correct checksum digit.
    If validation mode, return True if last digit checks correctly.
    """
    if verbose:
        print(f"\n{'Validation' if validate else 'Check digit'}",\
            f"calculations for {n}:\n\n i  nᵢ  p[i,nᵢ]   c\n------------------")
    # transform number list
    c, dig = 0, list(str(n if validate else 10 * n))
    for i, ni in enumerate(dig[::-1]):
        p = PERMUTATION_TABLE[i % 8][int(ni)]
        c = MULTIPLICATION_TABLE[c][p]
        if verbose:
            print(f"{i:2}  {ni}      {p}    {c}")

    if verbose and not validate:
        print(f"\ninv({c}) = {INV[c]}")
    if not terse:
        print(f"\nThe validation for '{n}' is {'correct' if c == 0 else 'incorrect'}."\
              if validate else f"\nThe check digit for '{n}' is {INV[c]}.")
    return c == 0 if validate else INV[c]

if __name__ == '__main__':

    for n, va, t, ve in [
        (236, False, False, True), (2363, True, False, True), (2369, True, False, True),
        (12345, False, False, True), (123451, True, False, True), (123459, True, False, True),
        (123456789012, False, False, False), (1234567890120, True, False, False),
        (1234567890129, True, False, False)]:
        verhoeffchecksum(n, va, t, ve)
Output:

Output same as Wren example.

R

Works with: R
Translation of: Julia
options(scipen=999)  # prevent scientific notation

multiplicationtable <- matrix(c(
  0,1,2,3,4,5,6,7,8,9,
  1,2,3,4,0,6,7,8,9,5,
  2,3,4,0,1,7,8,9,5,6,
  3,4,0,1,2,8,9,5,6,7,
  4,0,1,2,3,9,5,6,7,8,
  5,9,8,7,6,0,4,3,2,1,
  6,5,9,8,7,1,0,4,3,2,
  7,6,5,9,8,2,1,0,4,3,
  8,7,6,5,9,3,2,1,0,4,
  9,8,7,6,5,4,3,2,1,0
), nrow=10, byrow=TRUE)

permutationtable <- matrix(c(
  0,1,2,3,4,5,6,7,8,9,
  1,5,7,6,2,8,3,0,9,4,
  5,8,0,3,7,9,6,1,4,2,
  8,9,1,6,0,4,3,5,2,7,
  9,4,5,3,1,2,6,8,7,0,
  4,2,8,6,5,7,3,9,0,1,
  2,7,9,3,8,0,6,4,1,5,
  7,0,4,6,9,1,3,2,5,8
), nrow=8, byrow=TRUE)

inv <- c(0,4,3,2,1,5,6,7,8,9)

verhoeffchecksum <- function(n, validate=TRUE, terse=TRUE, verbose=FALSE) {
  nstr <- as.character(n)  # keep number as string
  if (verbose) {
    cat("\n", ifelse(validate, "Validation", "Check digit"),
        " calculations for '", nstr, "':\n\n",
        " i  nᵢ  p[i,nᵢ]  c\n------------------\n", sep="")
  }
  
  dig <- as.integer(strsplit(if (validate) nstr else paste0(nstr, "0"), "")[[1]])
  dig <- rev(dig)   # Julia uses reverse
  
  c <- 0
  for (i in seq_along(dig)) {
    ni <- dig[i]
    p <- permutationtable[(i - 1) %% 8 + 1, ni + 1]
    c <- multiplicationtable[c + 1, p + 1]
    if (verbose) {
      cat(sprintf("%2d  %d      %d     %d\n", i-1, ni, p, c))
    }
  }
  
  if (verbose && !validate) {
    cat("\ninv[", c, "] = ", inv[c + 1], "\n", sep="")
  }
  if (!terse) {
    if (validate) {
      cat("\nThe validation for '", nstr, "' is ",
          ifelse(c == 0, "correct", "incorrect"), ".\n", sep="")
    } else {
      cat("\nThe check digit for '", nstr, "' is '", inv[c + 1], "'\n", sep="")
    }
  }
  
  if (validate) {
    return(c == 0)
  } else {
    return(inv[c + 1])
  }
}

# Test runs
tests <- list(
  list("236", FALSE, FALSE, TRUE),
  list("2363", TRUE, FALSE, TRUE),
  list("2369", TRUE, FALSE, TRUE),
  list("12345", FALSE, FALSE, TRUE),
  list("123451", TRUE, FALSE, TRUE),
  list("123459", TRUE, FALSE, TRUE),
  list("123456789012", FALSE, FALSE),
  list("1234567890120", TRUE, FALSE),
  list("1234567890129", TRUE, FALSE)
)

for (args in tests) {
  do.call(verhoeffchecksum, args)
}
Output:

Check digit calculations for '236':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  0      0     0
 1  6      3     3
 2  3      3     1
 3  2      1     2

inv[2] = 3

The check digit for '236' is '3'

Validation calculations for '2363':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  3      3     3
 1  6      3     1
 2  3      3     4
 3  2      1     0

The validation for '2363' is correct.

Validation calculations for '2369':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  9      9     9
 1  6      3     6
 2  3      3     8
 3  2      1     7

The validation for '2369' is incorrect.

Check digit calculations for '12345':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  0      0     0
 1  5      8     8
 2  4      7     1
 3  3      6     7
 4  2      5     2
 5  1      2     4

inv[4] = 1

The check digit for '12345' is '1'

Validation calculations for '123451':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  1      1     1
 1  5      8     9
 2  4      7     2
 3  3      6     8
 4  2      5     3
 5  1      2     0

The validation for '123451' is correct.

Validation calculations for '123459':

 i  nᵢ  p[i,nᵢ]  c
------------------
 0  9      9     9
 1  5      8     1
 2  4      7     8
 3  3      6     2
 4  2      5     7
 5  1      2     5

The validation for '123459' is incorrect.

The check digit for '123456789012' is '0'

The validation for '1234567890120' is correct.

The validation for '1234567890129' is incorrect.



Generate the tables rather than hard coding, They're not all that complex.

my @d = [^10] xx 5;
@d[$_][^5].=rotate($_), @d[$_][5..*].=rotate($_) for 1..4;
push @d: [@d[$_].reverse] for flat 1..4, 0;

my @i = 0,4,3,2,1,5,6,7,8,9;

my %h = flat (0,1,5,8,9,4,2,7,0).rotor(2 =>-1).map({.[0]=>.[1]}), 6=>3, 3=>6;
my @p = [^10],;
@p.push: [@p[*-1].map: {%h{$_}}] for ^7;

sub checksum (Int $int where * ≥ 0, :$verbose = True ) {
    my @digits = $int.comb;
    say "\nCheckdigit calculation for $int:";
    say " i  ni  p(i, ni)  c" if $verbose;
    my ($i, $p, $c) = 0 xx 3;
    say " $i   0      $p     $c" if $verbose;
    for @digits.reverse {
        ++$i;
        $p = @p[$i % 8][$_];
        $c = @d[$c; $p];
        say "{$i.fmt('%2d')}   $_      $p     $c" if $verbose;
    }
    say "Checkdigit: {@i[$c]}";
    +($int ~ @i[$c]);
}

sub validate (Int $int where * ≥ 0, :$verbose = True) {
    my @digits = $int.comb;
    say "\nValidation calculation for $int:";
    say " i  ni  p(i, ni)  c" if $verbose;
    my ($i, $p, $c) = 0 xx 3;
    for @digits.reverse {
        $p = @p[$i % 8][$_];
        $c = @d[$c; $p];
        say "{$i.fmt('%2d')}   $_      $p     $c" if $verbose;
        ++$i;
    }
    say "Checkdigit: {'in' if $c}correct";
}

## TESTING

for 236, 12345, 123456789012 -> $int {
    my $check = checksum $int, :verbose( $int.chars < 8 );
    validate $check, :verbose( $int.chars < 8 );
    validate +($check.chop ~ 9), :verbose( $int.chars < 8 );
}
Output:
Checkdigit calculation for 236:
 i  ni  p(i, ni)  c
 0   0      0     0
 1   6      3     3
 2   3      3     1
 3   2      1     2
Checkdigit: 3

Validation calculation for 2363:
 i  ni  p(i, ni)  c
 0   3      3     3
 1   6      3     1
 2   3      3     4
 3   2      1     0
Checkdigit: correct

Validation calculation for 2369:
 i  ni  p(i, ni)  c
 0   9      9     9
 1   6      3     6
 2   3      3     8
 3   2      1     7
Checkdigit: incorrect

Checkdigit calculation for 12345:
 i  ni  p(i, ni)  c
 0   0      0     0
 1   5      8     8
 2   4      7     1
 3   3      6     7
 4   2      5     2
 5   1      2     4
Checkdigit: 1

Validation calculation for 123451:
 i  ni  p(i, ni)  c
 0   1      1     1
 1   5      8     9
 2   4      7     2
 3   3      6     8
 4   2      5     3
 5   1      2     0
Checkdigit: correct

Validation calculation for 123459:
 i  ni  p(i, ni)  c
 0   9      9     9
 1   5      8     1
 2   4      7     8
 3   3      6     2
 4   2      5     7
 5   1      2     5
Checkdigit: incorrect

Checkdigit calculation for 123456789012:
Checkdigit: 0

Validation calculation for 1234567890120:
Checkdigit: correct

Validation calculation for 1234567890129:
Checkdigit: incorrect
Translation of: Java
/* REXX implementation of the Verhoeff Algorithm  */
Call init                   -- fill the tables (data taken from Java Script)
Call check 236,1            -- compute check digit AND validate numbers
Call check 12345,1          -- "
Call check 123456789012,0   -- validate numbers
Exit

check:
  Parse Arg number,details
  numberx=number
  Say 'Check digit calculations for' number
  d=compute(number||0,details,1)
  Say 'The check digit for' numberx 'is' d
  Call check2 numberx||d,details
  Call check2 numberx||9,details
  Return

check2:
  Parse Arg number,details
  If details Then
    Say 'Validation calculations for' number
  d=compute(number,details,0)
  If d=0 Then
    Say 'The validation for' number' is correct.'
  Else
    Say 'The validation for' number' is incorrect.'
  Return

compute:
  Parse Arg number,details,show_inv
  Call details ''
  Call details ' i  ni  p[i, ni]  c'
  Call details '-------------------'
  c=0
  le=length(number)-1
  Do i=le To 0 By-1
    ni=substr(number,i+1,1)
    z=(le-i)//8
    pi=perm.z.ni
    c=mult.c.pi
    Call details right(le-i,2) right(ni,2) right(pi,7) right(c,5)
    End
  If show_inv Then
    Call details "inverse["c"] = "invt.c
  Return invt.c

details:
  If details Then
    Say arg(1)
  Return

init:
  Call mk_mult '[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]',
               ' [1, 2, 3, 4, 0, 6, 7, 8, 9, 5]',
               ' [2, 3, 4, 0, 1, 7, 8, 9, 5, 6]',
               ' [3, 4, 0, 1, 2, 8, 9, 5, 6, 7]',
               ' [4, 0, 1, 2, 3, 9, 5, 6, 7, 8]',
               ' [5, 9, 8, 7, 6, 0, 4, 3, 2, 1]',
               ' [6, 5, 9, 8, 7, 1, 0, 4, 3, 2]',
               ' [7, 6, 5, 9, 8, 2, 1, 0, 4, 3]',
               ' [8, 7, 6, 5, 9, 3, 2, 1, 0, 4]',
               ' [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]]'

  Call mk_invt '[0, 4, 3, 2, 1, 5, 6, 7, 8, 9]'

  Call mk_perm '[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]',
               ' [1, 5, 7, 6, 2, 8, 3, 0, 9, 4]',
               ' [5, 8, 0, 3, 7, 9, 6, 1, 4, 2]',
               ' [8, 9, 1, 6, 0, 4, 3, 5, 2, 7]',
               ' [9, 4, 5, 3, 1, 2, 6, 8, 7, 0]',
               ' [4, 2, 8, 6, 5, 7, 3, 9, 0, 1]',
               ' [2, 7, 9, 3, 8, 0, 6, 4, 1, 5]',
               ' [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]]'
  Return

mk_mult:
  Parse Arg list
  list=translate(list,'   ','[],')
  Do i=0 to 9
    Do j=0 to 9
      Parse Var list mult.i.j list
      End
    End
  Return

mk_invt:
  Parse Arg list
  list=translate(list,'   ','[],')
  Do i=0 to 9
    Parse Var list invt.i list
    End
  Return

mk_perm:
  Parse Arg list
  list=translate(list,'   ','[],')
  Do i=0 to 9
    Do j=0 to 9
      Parse Var list perm.i.j list
      End
    End
  Return

show_mult:
  If details Then Say'show_mult'
  Do i=0 To 9
    l=''
    Do j=1 To 9
      l=l mult.i.j
      End
    Call details l
    End
  return
Output:

Check digit calculations for 236

 i  ni  p[i, ni]  c
-------------------
 0  0       0     0
 1  6       3     3
 2  3       3     1
 3  2       1     2
inverse[2] = 3
The check digit for 236 is 3
Validation calculations for 2363

 i  ni  p[i, ni]  c
-------------------
 0  3       3     3
 1  6       3     1
 2  3       3     4
 3  2       1     0
The validation for 2363 is correct.
Validation calculations for 2369

 i  ni  p[i, ni]  c
-------------------
 0  9       9     9
 1  6       3     6
 2  3       3     8
 3  2       1     7
The validation for 2369 is incorrect.
Check digit calculations for 12345

 i  ni  p[i, ni]  c
-------------------
 0  0       0     0
 1  5       8     8
 2  4       7     1
 3  3       6     7
 4  2       5     2
 5  1       2     4
inverse[4] = 1
The check digit for 12345 is 1
Validation calculations for 123451

 i  ni  p[i, ni]  c
-------------------
 0  1       1     1
 1  5       8     9
 2  4       7     2
 3  3       6     8
 4  2       5     3
 5  1       2     0
The validation for 123451 is correct.
Validation calculations for 123459

 i  ni  p[i, ni]  c
-------------------
 0  9       9     9
 1  5       8     1
 2  4       7     8
 3  3       6     2
 4  2       5     7
 5  1       2     5
The validation for 123459 is incorrect.
Check digit calculations for 123456789012
The check digit for 123456789012 is 0
The validation for 1234567890120 is correct.
The validation for 1234567890129 is incorrect.
Translation of: C++
const MULTIPLICATION_TABLE: [[i32; 10]; 10] = [
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
    [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
    [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
    [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
    [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
    [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
    [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
    [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
    [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
];

const INVERSE: [i32; 10] = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];

const PERMUTATION_TABLE: [[i32; 10]; 8] = [
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
    [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
    [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
    [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
    [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
    [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
    [7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
];

fn verhoeff_checksum(number: &str, do_validation: bool, do_display: bool) -> i32 {
    if do_display {
        let calculation_type = if do_validation {
            "Validation"
        } else {
            "Check digit"
        };
        println!("{} calculations for {}\n", calculation_type, number);
        println!(" i  ni  p[i, ni]  c");
        println!("-------------------");
    }

    let mut working_number = String::from(number);
    if !do_validation {
        working_number.push('0');
    }

    let mut c = 0;
    let le = working_number.len() - 1;
    let chars: Vec<char> = working_number.chars().collect();
    
    for i in (0..=le).rev() {
        let ni = chars[i].to_digit(10).unwrap() as i32;
        let pos = (le - i) % 8;
        let pi = PERMUTATION_TABLE[pos][ni as usize];
        c = MULTIPLICATION_TABLE[c as usize][pi as usize];

        if do_display {
            println!(
                "{:2}  {:2}       {:2}     {:2}\n",
                le - i,
                ni,
                pi,
                c
            );
        }
    }

    if do_display && !do_validation {
        println!("inverse[{}] = {}\n", c, INVERSE[c as usize]);
    }

    if do_validation {
        if c == 0 { 1 } else { 0 }
    } else {
        INVERSE[c as usize]
    }
}

fn main() {
    let tests = [
        ("123", true),
        ("12345", true),
        ("123456789012", false),
    ];

    for &(test_num, display) in tests.iter() {
        let digit = verhoeff_checksum(test_num, false, display);
        println!("The check digit for {} is {}\n", test_num, digit);

        let numbers = [
            format!("{}{}", test_num, digit),
            format!("{}9", test_num),
        ];

        for number in numbers.iter() {
            let digit = verhoeff_checksum(number, true, display);
            let result = if digit == 1 { "correct" } else { "incorrect" };
            println!("The validation for {:?} is {:?}. ", number, result);
        }
    }
}
Output:
Check digit calculations for 123

 i  ni  p[i, ni]  c
-------------------
 0   0        0      0

 1   3        6      6

 2   2        0      6

 3   1        9      2

inverse[2] = 3

The check digit for 123 is 3

Validation calculations for 1233

 i  ni  p[i, ni]  c
-------------------
 0   3        3      3

 1   3        6      9

 2   2        0      9

 3   1        9      0

The validation for "1233" is "correct". 
Validation calculations for 1239

 i  ni  p[i, ni]  c
-------------------
 0   9        9      9

 1   3        6      3

 2   2        0      3

 3   1        9      7

The validation for "1239" is "incorrect". 
Check digit calculations for 12345

 i  ni  p[i, ni]  c
-------------------
 0   0        0      0

 1   5        8      8

 2   4        7      1

 3   3        6      7

 4   2        5      2

 5   1        2      4

inverse[4] = 1

The check digit for 12345 is 1

Validation calculations for 123451

 i  ni  p[i, ni]  c
-------------------
 0   1        1      1

 1   5        8      9

 2   4        7      2

 3   3        6      8

 4   2        5      3

 5   1        2      0

The validation for "123451" is "correct". 
Validation calculations for 123459

 i  ni  p[i, ni]  c
-------------------
 0   9        9      9

 1   5        8      1

 2   4        7      8

 3   3        6      2

 4   2        5      7

 5   1        2      5

The validation for "123459" is "incorrect". 
The check digit for 123456789012 is 0

The validation for "1234567890120" is "correct". 
The validation for "1234567890129" is "incorrect". 



Translation of: 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")
        }
    }
}
Output:
Identical to Wren example
Library: Wren-fmt
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.
Works with: Zig version 0.14.1
Translation of: Rust
const std = @import("std");
const print = std.debug.print;

const MULTIPLICATION_TABLE: [10][10]i32 = [_][10]i32{
    [_]i32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
    [_]i32{ 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 },
    [_]i32{ 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 },
    [_]i32{ 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 },
    [_]i32{ 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 },
    [_]i32{ 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 },
    [_]i32{ 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 },
    [_]i32{ 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 },
    [_]i32{ 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 },
    [_]i32{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
};

const INVERSE: [10]i32 = [_]i32{ 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 };

const PERMUTATION_TABLE: [8][10]i32 = [_][10]i32{
    [_]i32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
    [_]i32{ 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 },
    [_]i32{ 5, 8, 0, 3, 7, 9, 6, 1, 4, 2 },
    [_]i32{ 8, 9, 1, 6, 0, 4, 3, 5, 2, 7 },
    [_]i32{ 9, 4, 5, 3, 1, 2, 6, 8, 7, 0 },
    [_]i32{ 4, 2, 8, 6, 5, 7, 3, 9, 0, 1 },
    [_]i32{ 2, 7, 9, 3, 8, 0, 6, 4, 1, 5 },
    [_]i32{ 7, 0, 4, 6, 9, 1, 3, 2, 5, 8 },
};

fn verhoeffChecksum(allocator: std.mem.Allocator, number: []const u8, do_validation: bool, do_display: bool) !i32 {
    if (do_display) {
        const calculation_type = if (do_validation) "Validation" else "Check digit";
        print("{s} calculations for {s}\n\n", .{ calculation_type, number });
        print(" i  ni  p[i, ni]  c\n", .{});
        print("-------------------\n", .{});
    }

    var working_number = std.ArrayList(u8).init(allocator);
    defer working_number.deinit();

    try working_number.appendSlice(number);
    if (!do_validation) {
        try working_number.append('0');
    }

    var c: i32 = 0;
    const le = working_number.items.len - 1;

    var i: usize = le + 1;
    while (i > 0) {
        i -= 1;
        const ni = working_number.items[i] - '0';
        const pos = (le - i) % 8;
        const pi = PERMUTATION_TABLE[pos][ni];
        c = MULTIPLICATION_TABLE[@intCast(c)][@as(usize, @intCast(pi))];

        if (do_display) {
            print("{:2}  {:2}       {:2}     {:2}\n\n", .{ le - i, ni, pi, c });
        }
    }

    if (do_display and !do_validation) {
        print("inverse[{}] = {}\n\n", .{ c, INVERSE[@intCast(c)] });
    }

    if (do_validation) {
        return if (c == 0) 1 else 0;
    } else {
        return INVERSE[@intCast(c)];
    }
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const Test = struct {
        test_num: []const u8,
        display: bool,
    };

    const tests = [_]Test{
        .{ .test_num = "123", .display = true },
        .{ .test_num = "12345", .display = true },
        .{ .test_num = "123456789012", .display = false },
    };

    for (tests) |my_test| {
        const digit = try verhoeffChecksum(allocator, my_test.test_num, false, my_test.display);
        print("The check digit for {s} is {}\n\n", .{ my_test.test_num, digit });

        // Create test numbers with check digit and with '9'
        var correct_number = std.ArrayList(u8).init(allocator);
        defer correct_number.deinit();
        try correct_number.appendSlice(my_test.test_num);
        try correct_number.append('0' + @as(u8, @intCast(digit)));

        var incorrect_number = std.ArrayList(u8).init(allocator);
        defer incorrect_number.deinit();
        try incorrect_number.appendSlice(my_test.test_num);
        try incorrect_number.append('9');

        const numbers = [_][]const u8{ correct_number.items, incorrect_number.items };

        for (numbers) |number| {
            const validation_result = try verhoeffChecksum(allocator, number, true, my_test.display);
            const result = if (validation_result == 1) "correct" else "incorrect";
            print("The validation for \"{s}\" is \"{s}\". \n", .{ number, result });
        }
    }
}
Output:
Check digit calculations for 123

 i  ni  p[i, ni]  c
-------------------
 0   0       +0     +0

 1   3       +6     +6

 2   2       +0     +6

 3   1       +9     +2

inverse[2] = 3

The check digit for 123 is 3

Validation calculations for 1233

 i  ni  p[i, ni]  c
-------------------
 0   3       +3     +3

 1   3       +6     +9

 2   2       +0     +9

 3   1       +9     +0

The validation for "1233" is "correct". 
Validation calculations for 1239

 i  ni  p[i, ni]  c
-------------------
 0   9       +9     +9

 1   3       +6     +3

 2   2       +0     +3

 3   1       +9     +7

The validation for "1239" is "incorrect". 
Check digit calculations for 12345

 i  ni  p[i, ni]  c
-------------------
 0   0       +0     +0

 1   5       +8     +8

 2   4       +7     +1

 3   3       +6     +7

 4   2       +5     +2

 5   1       +2     +4

inverse[4] = 1

The check digit for 12345 is 1

Validation calculations for 123451

 i  ni  p[i, ni]  c
-------------------
 0   1       +1     +1

 1   5       +8     +9

 2   4       +7     +2

 3   3       +6     +8

 4   2       +5     +3

 5   1       +2     +0

The validation for "123451" is "correct". 
Validation calculations for 123459

 i  ni  p[i, ni]  c
-------------------
 0   9       +9     +9

 1   5       +8     +1

 2   4       +7     +8

 3   3       +6     +2

 4   2       +5     +7

 5   1       +2     +5

The validation for "123459" is "incorrect". 
The check digit for 123456789012 is 0

The validation for "1234567890120" is "correct". 
The validation for "1234567890129" is "incorrect".