Calculating checksum

If you have an argument as an character, i.e. #1=A, then `#1 will give you the ASCII number of this character. If you want that the character '0' gives you a numeric value of 0, and so on, you simply have to subtract the value `0 from each value. Luckily then characters for the digits are coded in numeric order in ASCII, i.e. `0-`1 = 1 etc.

I would loop over the input text yourself by putting it in front of an end-marker and reading one character a time in a recursive fashion.

\documentclass{article}
\usepackage{calc}

\newcounter{checksum}
\newcounter{weight}
\makeatletter
\newcommand\checksum[1]{%
    \setcounter{checksum}{0}%
    \setcounter{weight}{10}%
    \expandafter\@checksum#1\@nnil
    \loop\ifnum\value{checksum}>10
        \addtocounter{checksum}{-11}%
    \repeat
    \setcounter{checksum}{11-\value{checksum}}%
    \ifnum\value{checksum}=10
        \def\checksumdigit{X}%
    \else
    \ifnum\value{checksum}=11
        \def\checksumdigit{0}%
    \else
        \edef\checksumdigit{\arabic{checksum}}%
    \fi\fi
    \checksumdigit
}
% Reads the input one token a time, should only contains normal characters!
\def\@checksum#1{%
    \ifx\@nnil#1\relax\else % stop looping when endmarker is read
        \addtocounter{checksum}{\value{weight}*(`#1-`0)}%
        \addtocounter{weight}{-1}%
        \expandafter\@checksum % Recursive call => loop
    \fi
}
\makeatother




\begin{document}

\checksum{383480757}%5

\checksum{055215295}%1

\checksum{020113448}%9

\end{document}

This stores the checksum digit into \checksumdigit and prints it in the text. I tested it successfully on the three books above.


\documentclass{article}
\usepackage{xstring}
\def\GOODISBN#1{ISBN #1 is valid}
\def\BADISBN#1{ISBN #1 is invalid}

\makeatletter
\def\checkISBN#1{%
  \def\ISBN@arg{#1}%
  \StrDel{#1}{-}[\ISBN@temp]%
  \expandafter\StrLen\expandafter{\ISBN@temp}[\ISBN@length]%
  \ifnum\ISBN@length=10 
    \expandafter\checkISBNold\expandafter{\ISBN@temp}%
  \else
    \ifnum\ISBN@length=13
      \expandafter\checkISBNnew\expandafter{\ISBN@temp}%
    \else
      \BADISBN{\ISBN@arg}
    \fi
  \fi}

\def\checkISBNold#1{%
  \StrGobbleRight{#1}{1}[\ISBN@temp]%
  \StrRight{#1}{1}[\ISBN@check]%
  \@tempcnta=11 \@tempcntb=\z@
  \expandafter\@tfor\expandafter\next 
  \expandafter:\expandafter=\ISBN@temp\do
    {\advance\@tempcnta\m@ne
     \@tempcntb=\numexpr\@tempcntb+\next*\@tempcnta\relax
    }
  \@tempcnta=\@tempcntb
  \divide\@tempcnta by 11
  \multiply\@tempcnta by 11
  \advance\@tempcntb-\@tempcnta
  \@tempcntb=\numexpr11-\@tempcntb\relax
  \ifnum\@tempcntb=11
    \def\ISBN@final{0}%
  \else
    \ifnum\@tempcntb=10
      \def\ISBN@final{X}%
    \else
      \edef\ISBN@final{\number\@tempcntb}%
    \fi
  \fi
  \ifx\ISBN@final\ISBN@check
    \GOODISBN{\ISBN@arg}
  \else
    \BADISBN{\ISBN@arg}
  \fi  
}
\def\checkISBNnew#1{%
  \StrGobbleRight{#1}{1}[\ISBN@temp]%
  \StrRight{#1}{1}[\ISBN@check]%
  \@tempcnta=\z@ \@tempcntb=\z@
  \expandafter\@tfor\expandafter\next
  \expandafter:\expandafter=\ISBN@temp\do
    {\advance\@tempcnta\@ne
     \@tempcntb=\numexpr\@tempcntb+\next*\ifodd\@tempcnta 1\else 3\fi\relax
    }
  \@tempcnta=\@tempcntb
  \divide\@tempcnta by 10
  \multiply\@tempcnta by 10
  \advance\@tempcntb-\@tempcnta
  \@tempcntb=\numexpr10-\@tempcntb\relax
    \ifnum\@tempcntb=10
      \def\ISBN@final{0}%
    \else
      \edef\ISBN@final{\number\@tempcntb}%
   \fi
  \ifx\ISBN@final\ISBN@check
    \GOODISBN{\ISBN@arg}
  \else
    \BADISBN{\ISBN@arg}
  \fi
}

\begin{document}
\checkISBN{1000000011}

\checkISBN{1-00-000001-X}

\checkISBN{0-306-40615-2}

\checkISBN{978-0-306-40615-7}
\end{document}

The result is

ISBN 1000000011 is invalid
ISBN 1-00-000001-X is valid
ISBN 0-306-40615-2 is valid
ISBN 978-0-306-40615-7 is valid

The computation for old ISBN numbers can be streamlined:

\def\checkISBNold#1{%
  \@tempcnta=11 \@tempcntb=\z@
  \@tfor\next:=#1\do   
    {\advance\@tempcnta\m@ne
     \@tempcntb=\numexpr\@tempcntb+\if\next X10\else\next\fi*\@tempcnta\relax
    }
  \@tempcnta=\@tempcntb
  \divide\@tempcnta by 11
  \multiply\@tempcnta by 11 
  \advance\@tempcntb-\@tempcnta
  \ifnum\@tempcntb=\z@
    \GOODISBN{\ISBN@arg}
  \else
    \BADISBN{\ISBN@arg}
  \fi
}

Tags:

Calculations