Algorithm Implementation/Checksums/Damm Algorithm
Perl
editconst my @map => (
[0,3,1,7,5,9,8,6,4,2],
[7,0,9,2,1,5,4,8,6,3],
[4,2,0,6,8,7,1,3,5,9],
[1,7,5,0,9,8,3,4,2,6],
[6,1,2,3,0,4,5,9,7,8],
[3,6,7,4,2,0,9,5,8,1],
[5,8,6,9,7,2,0,1,3,4],
[8,9,4,5,3,6,2,0,1,7],
[9,4,3,8,6,1,7,2,0,5],
[2,5,8,1,4,3,6,7,9,0],
);
sub check_damm {
my $d = 0;
$d = $map[$d][$_] for split //,pop;
$d ? 0 : 1;
}
sub damm_digit {
my $d = 0;
$d = $map[$d][$_] for split //,pop;
$d;
}
C
edit/// <summary>
/// The Damm check digit
/// For more information cf. http://en.wikipedia.org/wiki/Damm_algorithm
/// </summary>
/// <remarks>
/// The array could be substituted with another
/// appropriate weak totally anti-symmetric quasigroup.
/// This would affect the check digit.
/// </remarks>
char Lookup(char* number)
{
const char table[]=
"0317598642"
"7092154863"
"4206871359"
"1750983426"
"6123045978"
"3674209581"
"5869720134"
"8945362017"
"9438617205"
"2581436790";
char interim='0';
char* p;
for(p=number;*p!='\0';++p){
if((unsigned char)(*p-'0')>9)
return '-'; //minus sign indicates an error: character is not a digit
interim=table[(*p-'0')+(interim-'0')*10];
}
return interim;
}
char CalculateCheckDigit(char* numberWithoutCheckDigit)
{
return Lookup(numberWithoutCheckDigit);
}
typedef int BOOL;
BOOL IsCheckDigitValid(char* numberWithCheckDigit)
{
return Lookup(numberWithCheckDigit)=='0';
}
Python
edit"""Damm algorithm decimal check digit
For reference see https://en.wikipedia.org/wiki/Damm_algorithm
"""
# we use the matrix given in the WP article because it's a good one
matrix = (
(0, 3, 1, 7, 5, 9, 8, 6, 4, 2),
(7, 0, 9, 2, 1, 5, 4, 8, 6, 3),
(4, 2, 0, 6, 8, 7, 1, 3, 5, 9),
(1, 7, 5, 0, 9, 8, 3, 4, 2, 6),
(6, 1, 2, 3, 0, 4, 5, 9, 7, 8),
(3, 6, 7, 4, 2, 0, 9, 5, 8, 1),
(5, 8, 6, 9, 7, 2, 0, 1, 3, 4),
(8, 9, 4, 5, 3, 6, 2, 0, 1, 7),
(9, 4, 3, 8, 6, 1, 7, 2, 0, 5),
(2, 5, 8, 1, 4, 3, 6, 7, 9, 0)
)
def encode(number):
number = str(number)
interim = 0
for digit in number:
interim = matrix[interim][int(digit)]
return interim
def check(number):
return encode(number) == 0
if __name__ == '__main__':
# quick sanity checking
assert encode(572) == 4 # from wikipedia
assert check(5724)
assert encode('43881234567') == 9 # hand-computed
C#
edit /// <summary>
/// The implementation of the damm algorithm based on the details on https://en.wikipedia.org/wiki/Damm_algorithm
/// </summary>
public static class DammAlgorithm
{
/// <summary>
/// The quasigroup table from https://en.wikipedia.org/wiki/Damm_algorithm
/// </summary>
static int[,] matrix = new int[,]
{
{0, 3, 1, 7, 5, 9, 8, 6, 4, 2},
{7, 0, 9, 2, 1, 5, 4, 8, 6, 3},
{4, 2, 0, 6, 8, 7, 1, 3, 5, 9},
{1, 7, 5, 0, 9, 8, 3, 4, 2, 6},
{6, 1, 2, 3, 0, 4, 5, 9, 7, 8},
{3, 6, 7, 4, 2, 0, 9, 5, 8, 1},
{5, 8, 6, 9, 7, 2, 0, 1, 3, 4},
{8, 9, 4, 5, 3, 6, 2, 0, 1, 7},
{9, 4, 3, 8, 6, 1, 7, 2, 0, 5},
{2, 5, 8, 1, 4, 3, 6, 7, 9, 0}
};
/// <summary>
/// Calculate the checksum digit from provided number
/// </summary>
/// <param name="number">the number</param>
/// <returns>Damm checksum</returns>
public static int CalculateCheckSum(string number)
{
var numbers = (from n in number select int.Parse(n.ToString()));
int interim = 0;
var en = numbers.GetEnumerator();
while (en.MoveNext())
{
interim = matrix[interim, en.Current];
}
return interim;
}
/// <summary>
/// Calculate the checksum digit from provided number
/// </summary>
/// <param name="number">the number</param>
/// <returns>Damm checksum</returns>
public static int CalculateCheckSum(int number)
{
return CalculateCheckSum(number.ToString());
}
/// <summary>
/// Calculate the checksum digit from provided number
/// </summary>
/// <param name="number">the number</param>
/// <returns>Damm checksum</returns>
public static int CalculateCheckSum(long number)
{
return CalculateCheckSum(number.ToString());
}
/// <summary>
/// Calculate the checksum digit from provided number and return the full number with the checksum
/// </summary>
/// <param name="number">the number</param>
/// <returns>full number with the Damm checksum</returns>
public static string GenerateCheckSum(string number)
{
var checkSumNumber = CalculateCheckSum(number);
return number + checkSumNumber.ToString();
}
/// <summary>
/// Calculate the checksum digit from provided number and return the full number with the checksum
/// </summary>
/// <param name="number">the number</param>
/// <returns>full number with the Damm checksum</returns>
public static int GenerateCheckSum(int number)
{
var checkSumNumber = CalculateCheckSum(number);
return (number * 10) + checkSumNumber;
}
/// <summary>
/// Calculate the checksum digit from provided number and return the full number with the checksum
/// </summary>
/// <param name="number">the number</param>
/// <returns>full number with the Damm checksum</returns>
public static long GenerateCheckSum(long number)
{
var checkSumNumber = CalculateCheckSum(number);
return (number * 10) + checkSumNumber;
}
/// <summary>
/// validates the number using the last digit as the Damm checksum
/// </summary>
/// <param name="number">the number to check</param>
/// <returns>True if valid; otherwise false</returns>
public static bool Validate(string number)
{
return CalculateCheckSum(number) == 0;
}
/// <summary>
/// validates the number using the last digit as the Damm checksum
/// </summary>
/// <param name="number">the number to check</param>
/// <returns>True if valid; otherwise false</returns>
public static bool Validate(int number)
{
return CalculateCheckSum(number) == 0;
}
/// <summary>
/// validates the number using the last digit as the Damm checksum
/// </summary>
/// <param name="number">the number to check</param>
/// <returns>True if valid; otherwise false</returns>
public static bool Validate(long number)
{
return CalculateCheckSum(number) == 0;
}
}
VBA
edit ' <summary>
' The implementation of the damm algorithm based on the details on https://en.wikipedia.org/wiki/Damm_algorithm
' </summary>
Public Function Encode(ByVal number As String) As Byte
Dim i As Long
Dim interim As Byte
Dim matrix
matrix = Array( _
Array(0, 3, 1, 7, 5, 9, 8, 6, 4, 2), _
Array(7, 0, 9, 2, 1, 5, 4, 8, 6, 3), _
Array(4, 2, 0, 6, 8, 7, 1, 3, 5, 9), _
Array(1, 7, 5, 0, 9, 8, 3, 4, 2, 6), _
Array(6, 1, 2, 3, 0, 4, 5, 9, 7, 8), _
Array(3, 6, 7, 4, 2, 0, 9, 5, 8, 1), _
Array(5, 8, 6, 9, 7, 2, 0, 1, 3, 4), _
Array(8, 9, 4, 5, 3, 6, 2, 0, 1, 7), _
Array(9, 4, 3, 8, 6, 1, 7, 2, 0, 5), _
Array(2, 5, 8, 1, 4, 3, 6, 7, 9, 0)) _
For i = 1 To Len(number)
interim = matrix(interim)(CByte(Mid(number, i, 1)))
Next i
Encode = interim
End Function
Public Function check(ByVal number as String) as Boolean
If encode(number) = 0 then
check = True
Else
check = False
End if
End Function
PHP
edit<?php
/**
* The implementation of the damm algorithm based on the details on https://en.wikipedia.org/wiki/Damm_algorithm
*
* Class DammAlgorithm
*/
class DammAlgorithm
{
/**
* The quasigroup table from http://www.md-software.de/math/DAMM_Quasigruppen.txt
*
* @var $matrix array
*/
protected static $matrix = array(
array(0, 3, 1, 7, 5, 9, 8, 6, 4, 2),
array(7, 0, 9, 2, 1, 5, 4, 8, 6, 3),
array(4, 2, 0, 6, 8, 7, 1, 3, 5, 9),
array(1, 7, 5, 0, 9, 8, 3, 4, 2, 6),
array(6, 1, 2, 3, 0, 4, 5, 9, 7, 8),
array(3, 6, 7, 4, 2, 0, 9, 5, 8, 1),
array(5, 8, 6, 9, 7, 2, 0, 1, 3, 4),
array(8, 9, 4, 5, 3, 6, 2, 0, 1, 7),
array(9, 4, 3, 8, 6, 1, 7, 2, 0, 5),
array(2, 5, 8, 1, 4, 3, 6, 7, 9, 0),
);
/**
* Calculate the checksum digit from provided number
*
* @param $number
* @return int
*/
public static function encode($number) {
/* @var $interim int */
$interim = 0;
/* @var $i int */
for ($i=0; $i<strlen($number); $i++) {
$interim = self::$matrix[$interim][$number[$i]];
}
return $interim;
}
/**
* Checks the checksum digit from provided number
*
* @param $number
* @return bool
*/
public static function check($number) {
return (0 == self::encode($number));
}
}
Java
edit/**
* The implementation of the damm algorithm based on the details on https://en.wikipedia.org/wiki/Damm_algorithm
*/
public class Damm10ChecksumDigit {
/**
* The quasigroup table from https://en.wikipedia.org/wiki/Damm_algorithm
*/
private static int[][] matrix = new int[][] {
{ 0, 3, 1, 7, 5, 9, 8, 6, 4, 2 },
{ 7, 0, 9, 2, 1, 5, 4, 8, 6, 3 },
{ 4, 2, 0, 6, 8, 7, 1, 3, 5, 9 },
{ 1, 7, 5, 0, 9, 8, 3, 4, 2, 6 },
{ 6, 1, 2, 3, 0, 4, 5, 9, 7, 8 },
{ 3, 6, 7, 4, 2, 0, 9, 5, 8, 1 },
{ 5, 8, 6, 9, 7, 2, 0, 1, 3, 4 },
{ 8, 9, 4, 5, 3, 6, 2, 0, 1, 7 },
{ 9, 4, 3, 8, 6, 1, 7, 2, 0, 5 },
{ 2, 5, 8, 1, 4, 3, 6, 7, 9, 0 } };
/**
* Calculate the checksum digit from provided number
*
* @param number
* @return calculated Damm checksum digit
*/
public static int calculateCheckSumDigit(String number) {
int interim = 0;
for (int index = 0; index < number.length(); index++) {
char currCh = number.charAt(index);
if (! Character.isDigit(currCh)) {
throw new RuntimeException(number + " is not a valid number");
}
int currentIndex = currCh - 48;
interim = matrix[interim][currentIndex];
}
return interim;
}
/**
* Calculate the checksum digit from provided number
* @param number
* @return calculated Damm checksum digit
*/
public static int calculateCheckSumDigit(int number) {
return calculateCheckSumDigit(String.valueOf(number));
}
/**
* Calculate the checksum digit from provided number
* @param number
* @return calculated Damm checksum digit
*/
public static int calculateCheckSumDigit(long number) {
return calculateCheckSumDigit(String.valueOf(number));
}
/**
* Calculate the checksum digit from provided number and return the full
* number with the checksum
* @param number
* @return full number with the Damm checksum
*/
public static String generateCheckSum(String number) {
int checkSumDigit = calculateCheckSumDigit(number);
return number + String.valueOf(checkSumDigit);
}
/**
* Calculate the checksum digit from provided number and return the full
* number with the checksum
* @param number
* @return full number with the Damm checksum
*/
public static int generateCheckSum(int number) {
int checkSumDigit = calculateCheckSumDigit(number);
return (number * 10) + checkSumDigit;
}
/**
* Calculate the checksum digit from provided number and return the full
* number with the checksum
* @param number
* @return full number with the Damm checksum
*/
public static long generateCheckSum(long number) {
int checkSumNumber = calculateCheckSumDigit(number);
return (number * 10) + checkSumNumber;
}
/**
* validates the number using the last digit as the Damm checksum
*
* @param number
* @return True if valid; otherwise false
*/
public static boolean validate(String number) {
return calculateCheckSumDigit(number) == 0;
}
/**
* validates the number using the last digit as the Damm checksum
*
* @param number
* @return True if valid; otherwise false
*/
public static boolean validate(int number) {
return calculateCheckSumDigit(number) == 0;
}
/**
* validates the number using the last digit as the Damm checksum
*
* @param number
* @return True if valid; otherwise false
*/
public static boolean validate(long number) {
return calculateCheckSumDigit(number) == 0;
}
}
Ruby
editmodule Damm
# See https://en.wikipedia.org/wiki/Damm_algorithm
TABLE=["0317598642","7092154863","4206871359","1750983426","6123045978",
"3674209581","5869720134","8945362017","9438617205","2581436790"]
def lookup number
number.to_s.each_char.inject(0) {|m,v|
TABLE[m][v.to_i].to_i
}
end
def check number
lookup(number.to_i/10)==number%10
end
def generate number
number.to_i*10+lookup(number.to_i)
end
extend(Damm)
end
Scala
editobject Damm {
private val matrix = Array(
Array(0, 3, 1, 7, 5, 9, 8, 6, 4, 2),
Array(7, 0, 9, 2, 1, 5, 4, 8, 6, 3),
Array(4, 2, 0, 6, 8, 7, 1, 3, 5, 9),
Array(1, 7, 5, 0, 9, 8, 3, 4, 2, 6),
Array(6, 1, 2, 3, 0, 4, 5, 9, 7, 8),
Array(3, 6, 7, 4, 2, 0, 9, 5, 8, 1),
Array(5, 8, 6, 9, 7, 2, 0, 1, 3, 4),
Array(8, 9, 4, 5, 3, 6, 2, 0, 1, 7),
Array(9, 4, 3, 8, 6, 1, 7, 2, 0, 5),
Array(2, 5, 8, 1, 4, 3, 6, 7, 9, 0)
)
/**
* Calculates the checksum from the provided string
* @param str a string, only the numerics will be calculated
* @return
*/
def encode(str: String): Int = {
@tailrec def fn(interim: Int, idx: Int): Int = {
if (idx >= str.length) {
interim
} else {
val c = str.charAt(idx)
// only push numerics...
fn( if (c.isDigit) matrix(interim)(c - 48) else interim, idx + 1)
}
}
fn (0, 0)
}
/**
* Decorates the string with the checksum
* @param str
* @return
*/
def apply(str: String): String = str + encode(str).toString
/**
* Unapply method returning the string without the checksum if it matches otherwise None
* @param str
* @return
*/
def unapply(str: String): Option[String] =
if (isValid(str)) Some(str.substring(0, str.length - 1)) else None
/**
* Determines if the string contains a valid checksum
* @param str
* @return
*/
def isValid(str: String): Boolean = encode(str) == 0
}
JavaScript
edit// Taken from Wikipedia: Damm_Algorithm
var table = [
[0, 3, 1, 7, 5, 9, 8, 6, 4, 2],
[7, 0, 9, 2, 1, 5, 4, 8, 6, 3],
[4, 2, 0, 6, 8, 7, 1, 3, 5, 9],
[1, 7, 5, 0, 9, 8, 3, 4, 2, 6],
[6, 1, 2, 3, 0, 4, 5, 9, 7, 8],
[3, 6, 7, 4, 2, 0, 9, 5, 8, 1],
[5, 8, 6, 9, 7, 2, 0, 1, 3, 4],
[8, 9, 4, 5, 3, 6, 2, 0, 1, 7],
[9, 4, 3, 8, 6, 1, 7, 2, 0, 5],
[2, 5, 8, 1, 4, 3, 6, 7, 9, 0]
];
function calc(number) {
var str = number + '',
sum = 0;
for (var i = 0; i < str.length; ++i) {
sum = table[sum][str[i]];
}
return sum;
}
function check(number) {
return calc(number) == 0;
}
Elixir
editdefmodule Damm do
@table [
"0317598642","7092154863","4206871359","1750983426","6123045978",
"3674209581","5869720134","8945362017","9438617205","2581436790"
]
|> Enum.map( fn cs ->
to_charlist( cs )
|> Enum.map( &(&1-?0) )
|> List.to_tuple
end )
|> List.to_tuple
def table, do: @table
def lookup(number) do
number
|> Integer.digits
|> Enum.reduce( 0, fn d,a ->
elem(@table,a) |> elem(d)
end)
end
def check(number) do
lookup(Integer.floor_div(number,10))==Integer.mod(number,10)
end
def generate(number) do
number*10 + lookup( number )
end
end
Pascal
editconst
Damm_Array: array[0..9, 0..9] of Byte = (
(0,3,1,7,5,9,8,6,4,2),
(7,0,9,2,1,5,4,8,6,3),
(4,2,0,6,8,7,1,3,5,9),
(1,7,5,0,9,8,3,4,2,6),
(6,1,2,3,0,4,5,9,7,8),
(3,6,7,4,2,0,9,5,8,1),
(5,8,6,9,7,2,0,1,3,4),
(8,9,4,5,3,6,2,0,1,7),
(9,4,3,8,6,1,7,2,0,5),
(2,5,8,1,4,3,6,7,9,0)
);
function CalcDamm(Num: String): Char; overload;
var
interim: Byte;
i: Integer;
begin
interim := 0;
for i := 1 to Length(Num) do
interim := Damm_Array[interim, Ord(Num[i]) - $30];
Result := Char(interim + $30);
end;
function AddDamm(Num: String): String; overload;
begin
if Num <> '' then
Result := Num + CalcDamm(Num)
else
Result := '';
end;
function ValidDamm(Num: String): Boolean; overload;
begin
if num <> '' then
Result := CalcDamm(Num) = '0'
else
Result := False;
end;
R
editDamm_digit <- function(IDvar){
nondig <- simpleError("The function Damm_digit() stopped, because the submitted ID vector contains non-digit characters.")
if (sum(grepl("[^0-9]", IDvar)) > 0) stop(nondig)
quasigroup <- matrix( data = as.integer(c( 0, 3, 1, 7, 5, 9, 8, 6, 4, 2
, 7, 0, 9, 2, 1, 5, 4, 8, 6, 3
, 4, 2, 0, 6, 8, 7, 1, 3, 5, 9
, 1, 7, 5, 0, 9, 8, 3, 4, 2, 6
, 6, 1, 2, 3, 0, 4, 5, 9, 7, 8
, 3, 6, 7, 4, 2, 0, 9, 5, 8, 1
, 5, 8, 6, 9, 7, 2, 0, 1, 3, 4
, 8, 9, 4, 5, 3, 6, 2, 0, 1, 7
, 9, 4, 3, 8, 6, 1, 7, 2, 0, 5
, 2, 5, 8, 1, 4, 3, 6, 7, 9, 0))
, ncol = 10
, byrow = TRUE
)
IDdig <- lapply(strsplit(as.character(IDvar), ""), as.integer)
chk_digit <- integer()
for (i in seq_along(IDdig)){
tmp_digit <- 0
for (j in IDdig[[i]]) tmp_digit <- quasigroup[tmp_digit+1, j+1]
chk_digit <- c(chk_digit, tmp_digit)
}
return(chk_digit)
}
Damm_encode <- function(IDvar) IDvar * 10 + Damm_digit(IDvar)
Damm_check <- function(IDvar) Damm_digit(IDvar) == 0
Google Sheets
editThis code also works with Microsoft Excel if you remove the arrayformula().
=REDUCE(0,arrayformula(MID(A1,SEQUENCE(1,LEN(A1)),1)),lambda(i,j,INDEX({0,3,1,7,5,9,8,6,4,2;7,0,9,2,1,5,4,8,6,3;4,2,0,6,8,7,1,3,5,9;1,7,5,0,9,8,3,4,2,6;6,1,2,3,0,4,5,9,7,8;3,6,7,4,2,0,9,5,8,1;5,8,6,9,7,2,0,1,3,4;8,9,4,5,3,6,2,0,1,7;9,4,3,8,6,1,7,2,0,5;2,5,8,1,4,3,6,7,9,0},i+1,j+1)))
For locations that use comma as the decimal separator:
=REDUCE(0;arrayformula(MID(A1;SEQUENCE(1;LEN(A1));1));lambda(i;j;INDEX({0\3\1\7\5\9\8\6\4\2;7\0\9\2\1\5\4\8\6\3;4\2\0\6\8\7\1\3\5\9;1\7\5\0\9\8\3\4\2\6;6\1\2\3\0\4\5\9\7\8;3\6\7\4\2\0\9\5\8\1;5\8\6\9\7\2\0\1\3\4;8\9\4\5\3\6\2\0\1\7;9\4\3\8\6\1\7\2\0\5;2\5\8\1\4\3\6\7\9\0};i+1;j+1)))