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 <https://en.wikipedia.org/wiki/CUSIP>.

   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 <https://en.wikipedia.org/wiki/Damm_algorithm> (Damm,
   not Damn).

   Math::CheckDigits implements yet another approach to check digits.

   Neil Bowers has also written a review of LUHN modules
   <http://neilb.org/reviews/luhn.html>, which covers them in more detail
   than this section.

REPOSITORY
   <https://github.com/krschwab/Algorithm-LUHN_XS>

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:
   <https://perlmonks.org/?node_id=1226543>, and I received help from
   several PerlMonks members:

       AnomalousMonk <https://perlmonks.org/?node_id=634253>

       BrowserUK <https://perlmonks.org/?node_id=171588>

       Corion <https://perlmonks.org/?node_id=5348>

       LanX <https://perlmonks.org/?node_id=708738>

       tybalt89 <https://perlmonks.org/?node_id=1172229>