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
}