Verhoeff algorithm

From Rosetta Code
Task
Verhoeff algorithm
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

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.

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

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.

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

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 

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

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.

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)

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

Wren

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.