NAME
Algorithm::LUHN_XS - Very Fast XS Version of the original
Algorithm::LUHN
SYNOPSIS
use Algorithm::LUHN_XS qw/check_digit is_valid/;
my $c;
$c = check_digit("43881234567");
print "It works\n" if is_valid("43881234567$c");
$c = check_digit("A2C4E6G8"); # this will return undef
if (!defined($c)) {
# couldn't create a check digit
}
print "Valid LUHN characters are:\n";
my %vc = Algorithm::LUHN_XS::valid_chars();
for (sort keys %vc) {
print "$_ => $vc{$_}\n";
}
Algorithm::LUHN_XS::valid_chars(map {$_ => ord($_)-ord('A')+10} A..Z);
$c = check_digit("A2C4E6G8");
print "It worked again\n" if is_valid("A2C4E6G8$c");
DESCRIPTION
This module is an XS version of the original Perl Module
Algorithm::LUHN, which was written by Tim Ayers. It should work exactly
the same, only substantially faster. The supplied check_digit() routine
is 100% compatible with the pure Perl Algorithm::LUHN module, while the
faster check_digit_fast() and really fast check_digit_rff() are not.
How much faster? Here's a benchmark, running on a 3.4GHz i7-2600:
"Benchmark: timing 100 iterations"
"Algorithm::LUHN: 69 secs (69.37 usr 0.00 sys) 1.44/s"
"check_digit: 2 secs ( 1.98 usr 0.00 sys) 50.51/s"
"check_digit_fast: 2 secs ( 1.68 usr 0.00 sys) 59.52/s"
"check_digit_rff: 1 secs ( 1.29 usr 0.00 sys) 77.52/s"
So, it's 35x to 53x faster than the original pure Perl module, depending
on how much compatibility with the original module you need.
The rest of the documentation is mostly a copy of the original docs,
with some additions for functions that are new.
This module calculates the Modulus 10 Double Add Double checksum, also
known as the LUHN Formula. This algorithm is used to verify credit card
numbers and Standard & Poor's security identifiers such as CUSIP's and
CSIN's.
You can find plenty of information about the algorithm by searching the
web for "modulus 10 double add double".
FUNCTION
is_valid CHECKSUMMED_NUM
This function takes a credit-card number and returns true if the
number passes the LUHN check.
Ie it returns true if the final character of CHECKSUMMED_NUM is the
correct checksum for the rest of the number and false if not.
Obviously the final character does not factor into the checksum
calculation. False will also be returned if NUM contains in an
invalid character as defined by valid_chars(). If NUM is not valid,
$Algorithm::LUHN_XS::ERROR will contain the reason.
This function is equivalent to
substr $N,length($N)-1 eq check_digit(substr $N,0,length($N)-1)
For example, "4242 4242 4242 4242" is a valid Visa card number, that
is provided for test purposes. The final digit is '2', which is the
right check digit. If you change it to a '3', it's not a valid card
number. Ie:
is_valid('4242424242424242'); # true
is_valid('4242424242424243'); # false
is_valid_fast CHECKSUMMED_NUM
is_valid_rff CHECKSUMMED_NUM
As with check_digit(), we have 3 versions of is_valid(), each one
progressively faster than the check_digit() that comes in the
original pure Perl Algorithm::LUHN module. Here's a benchmark of 1M
total calls to is_valid():
"Benchmark: timing 100 iterations"
"Algorithm::LUHN: 100 secs (100.29 usr 0.01 sys) 1.00/s"
"is_valid: 3 secs ( 2.46 usr 0.11 sys) 38.91/s"
"is_valid_fast: 2 secs ( 2.38 usr 0.05 sys) 41.15/s"
"is_valid_rff: 2 secs ( 1.97 usr 0.08 sys) 48.78/s"
Algorithm::LUHN_XS varies from 38x to 48x times faster than the
original pure perl Algorithm::LUHN module. The is_valid() routine is
100% compatible with the original, returning either '1' for success
or the empty string '' for failure. The is_valid_fast() routine
returns 1 for success and 0 for failure. Finally, the is_valid_rff()
function also returns 1 for success and 0 for failure, but only
works with numeric input. If you supply any alpha characters, it
will return 0.
check_digit NUM
This function returns the checksum of the given number. If it cannot
calculate the check_digit it will return undef and set
$Algorithm::LUHN_XS::ERROR to contain the reason why. This is much
faster than the check_digit routine in the pure perl Algorithm::LUHN
module, but only about half as fast as the check_digit_fast()
function in this module, due to the need to return both integers and
undef, which isn't fast with XS.
check_digit_fast NUM
This function returns the checksum of the given number. If it cannot
calculate the check digit it will return -1 and set
$Algorithm::LUHN_XS::ERROR to contain the reason why. It's about 20%
faster than check_digit() because the XS code in this case only has
to return integers.
check_digit_rff NUM
This function returns the checksum of the given number.
It's about 50% faster than check_digit() because it doesn't support
the valid_chars() function, and only produces a valid output for
numeric input. If you pass it input with alpha characters, it will
return -1. Works great for Credit Cards, but not for things like
CUSIP identifiers .
valid_chars LIST
By default this module only recognizes 0..9 as valid characters, but
sometimes you want to consider other characters as valid, e.g.
Standard & Poor's identifers may contain 0..9, A..Z, @, #, *. This
function allows you to add additional characters to the accepted
list.
LIST is a mapping of "character" => "value". For example, Standard &
Poor's maps A..Z to 10..35 so the LIST to add these valid characters
would be (A, 10, B, 11, C, 12, ...)
Please note that this *adds* or *re-maps* characters, so any
characters already considered valid but not in LIST will remain
valid.
If you do not provide LIST, this function returns the current valid
character map.
Note that the check_digit_rff() and is_valid_rff() functions do not
support the valid_chars() function. Both only support numeric
inputs, and map them to their literal values.
CAVEATS
This module, because of how valid_chars() stores data in the XS portion,
is NOT thread safe.
The _fast and _rff versions of is_valid() and check_digit() don't have
the same return values for failure as the original Algorithm::LUHN
module. Specifically:
* is_valid_fast() and is_valid_rff() return 0 on failure, but
is_valid() returns the empty string.
* check_digit_fast() and check_digit_rff() return -1 on failure, but
check_digit() returns undef.
Also, be careful with passing long numbers around. Perl will, depending
on the context, convert things like 12345678912345 to
1.2345678912345e+1. Try to keep things in "string context".
SEE ALSO
Algorithm::LUHN is the original pure perl module this is based on.
Algorithm::CheckDigits provides a front-end to a large collection of
modules for working with check digits.
Business::CreditCard provides three functions for checking credit card
numbers. Business::CreditCard::Object provides an OO interface to those
functions.
Business::CardInfo provides a class for holding credit card details, and
has a type constraint on the card number, to ensure it passes the LUHN
check.
Business::CCCheck provides a number of functions for checking credit
card numbers.
Regexp::Common supports combined LUHN and issuer checking against a card
number.
Algorithm::Damm implements a different kind of check digit algorithm,
the Damm algorithm (Damm,
not Damn).
Math::CheckDigits implements yet another approach to check digits.
Neil Bowers has also written a review of LUHN modules
, which covers them in more detail
than this section.
REPOSITORY
AUTHOR
This module was written by Kerry Schwab
(http://search.cpan.org/search?author=KSCHWAB).
COPYRIGHT
Copyright (c) 2018 Kerry Schwab. All rights reserved. Derived from
Algorithm::LUHN, which is (c) 2001 by Tim Ayers.
LICENSE
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
CREDITS
Tim Ayers, for the original pure perl version of Algorithm::LUHN.
Neil Bowers, the current maintainer of Algorithm::LUHN.
The inspiration for this module was a PerlMonks post I made here:
, and I received help from
several PerlMonks members:
AnomalousMonk
BrowserUK
Corion
LanX
tybalt89