Jump to content

Algorithm Implementation/Checksums/Damm Algorithm

From Wikibooks, open books for an open world
const 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;
}
/// <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 | edit source]
"""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
    /// <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;
        }
    }
    ' <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
/**
 * 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));
    }
}
/**
 * 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;
    }
}
module 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

[edit | edit source]
object 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 | edit source]
// 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

[edit | edit source]
defmodule 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

[edit | edit source]
const
  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;
Damm_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

[edit | edit source]

This 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)))