Algorithm::Verhoeff - Perl extension for checking and computing Verhoeff


Algorithm-Verhoeff documentation Contained in the Algorithm-Verhoeff distribution.

Index


Code Index:

NAME

Top

Algorithm::Verhoeff - Perl extension for checking and computing Verhoeff check digits

SYNOPSIS

Top

  use Algorithm::Verhoeff;
  my $long_number = 1456789;
  # add a check digit to that to catch typos!

  $long_number .= verhoeff_get($long_number); # note - append don't add!
  print $long_number; #prints 14567894

  # Lets see if I can re-type that accurately
  my $test = '14657894'; # oops!
  unless (verhoeff_check($test))
  {
    print "Failed check - typo?";
  }

DESCRIPTION

Top

This implements the Verhoeff check digit algorithm. It's a single digit checksum designed specifically for catching data entry mistakes in number sequences. It catches the vast majority of common mistakes such as transposed digits, ommitted digits, double entered digits and so on.

EXPORT

By default, this module will export verhoeff_check() verhoeff_get()

Into the current package.

USAGE

Top

Using numbers that pass the verhoeff check is useful for things like product codes. This is because such numbers almost never pass the verhoeff check if they as mis-typed. This includes common typos such as ommitted or repeated digits, transposed digits and so on. Since it only adds a single digit onto what might already be a longish number, it's a good algorithm for use where humans need to enter or read the numbers.

When we say 'number' we really mean 'string of digits' since that is what the Verhoeff algorithm works on.

To generate such a number, pick a starting number, call verhoeff_check() to get a check digit, and then APPEND that digit to the end of the original number. Do NOT add the digit arithmetically to the original number.

The new number will how pass the verhoeff_check(). In other words, if verhoeff_check() is called with the new number as its argument, it will return 1. If it is called with a mistyped version of the original number it will (very probably) return 0. For common forms of typo such as ommitted digits, accuracy is over 99%.

verhoeff_get($number)

verhoeff_get accepts a number or string as an argument, and returns a check digit between 0 and 9.

verhoeff_check($number)

verhoeff_check accepts a number of string as an argument, and returns 1 if it passes the check, or 0 otherwise.

WARNING

Top

Both functions convert their argument to a string internally. This will break for large (<32 bit)numbers due to Perl representing them in standard form when stringified. To get round this, either pass the number as a string to begin with, or use the bignum module in your program.

Thus:

    my $num = 57382957482395748329574923; # Big number!
    verhoeff_get($num); # Fatal error unless program uses bignum module.

But:

    my $num = '57382957482395748329574923'; # Long string!
    verhoeff_get($num); # Works fine.

SEE ALSO

Top

bignum

AUTHOR

Top

Jon Peterson (jon -at- snowdrift.org)

COPYRIGHT AND LICENSE

Top


Algorithm-Verhoeff documentation Contained in the Algorithm-Verhoeff distribution.

package Algorithm::Verhoeff;

use 5.0;
use strict;
use warnings;
#use bignum; # Needed so large numbers don't get turned into standard form

require Exporter;

our @ISA = qw(Exporter);

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration	use Algorithm::Verhoeff ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
    verhoeff_get verhoeff_check
) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(
    verhoeff_get verhoeff_check
);

our $VERSION = '0.3';


# Preloaded methods go here.

our $di; #Dihedral matrix
our @inverted =  (0, 4, 3, 2, 1, 5, 6, 7, 8, 9 );
our $f;

# First, build $f according to a simple(?) equation
BEGIN{
$f->[0] = [(0 .. 9)];
$f->[1] = [( 1, 5, 7, 6, 2, 8, 3, 0, 9, 4 )];
my $i=2;
my $j=0;
while($i < 8)
{
    while($j < 10)
    {
        $f->[$i]->[$j] = $f->[$i - 1]->[$f->[1]->[$j]];
        $j++;
    }
    $j = 0;
    $i++;
}

# This is defined
$di->[0] = [( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )];
$di->[1] = [( 1, 2, 3, 4, 0, 6, 7, 8, 9, 5 )];
$di->[2] = [( 2, 3, 4, 0, 1, 7, 8, 9, 5, 6 )];
$di->[3] = [( 3, 4, 0, 1, 2, 8, 9, 5, 6, 7 )];
$di->[4] = [( 4, 0, 1, 2, 3, 9, 5, 6, 7, 8 )];
$di->[5] = [( 5, 9, 8, 7, 6, 0, 4, 3, 2, 1 )];
$di->[6] = [( 6, 5, 9, 8, 7, 1, 0, 4, 3, 2 )];
$di->[7] = [( 7, 6, 5, 9, 8, 2, 1, 0, 4, 3 )];
$di->[8] = [( 8, 7, 6, 5, 9, 3, 2, 1, 0, 4 )];
$di->[9] = [( 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 )];

}

# Now that's all set up, we can actually do stuff.

sub verhoeff_check
{
    my $input = shift;
    my $c = 0; # initialize check at 0
    my $digit;
    my $i = 0;
    foreach $digit (reverse split(//, $input))
    {
        $c = $di->[$c]->[$f->[$i % 8]->[$digit]]; # did you catch that?
        $i++;
    }
    if($c)
    {
        return 0; # a non-zero value of $c is a check failure
    }
    return 1;
}

sub verhoeff_get
{
    my $input = shift;
    my $c = 0; # initialize check at 0
    my $digit = 0;
    my $i = 0; my $r;
    foreach $digit (reverse split(//, $input))
    {
        $c = $di->[$c]->[$f->[($i+1) % 8]->[$digit]]; # not quite the same...
        $i++;
    }
    return $inverted[$c];
}
1;
__END__