File Coverage

blib/lib/Algorithm/LUHN_XS.pm
Criterion Covered Total %
statement 20 20 100.0
branch 2 2 100.0
condition n/a
subroutine 6 6 100.0
pod 1 1 100.0
total 29 29 100.0


line stmt bran cond sub pod time code
1             package Algorithm::LUHN_XS;
2             $Algorithm::LUHN_XS::VERSION = '1.08';
3             require XSLoader;
4             XSLoader::load('Algorithm::LUHN_XS', $VERSION);
5 3     3   18507 use 5.006;
  3         24  
6 3     3   18 use strict;
  3         6  
  3         65  
7 3     3   14 use warnings;
  3         6  
  3         110  
8 3     3   18 use Exporter;
  3         4  
  3         1199  
9              
10             our @ISA = qw/Exporter/;
11             our @EXPORT = qw//;
12             our @EXPORT_OK = qw/check_digit check_digit_fast check_digit_rff
13             is_valid is_valid_fast is_valid_rff
14             valid_chars/;
15             our $ERROR;
16              
17             # The hash of valid characters.
18             my %map = map { $_ => $_ } 0..9;
19             valid_chars(%map);
20             _al_init_vc(\%map);
21              
22             =pod
23              
24             =head1 NAME
25              
26             Algorithm::LUHN_XS - Very Fast XS Version of the original Algorithm::LUHN
27              
28             =head1 SYNOPSIS
29              
30             use Algorithm::LUHN_XS qw/check_digit is_valid/;
31              
32             my $c;
33             $c = check_digit("43881234567");
34             print "It works\n" if is_valid("43881234567$c");
35              
36             $c = check_digit("A2C4E6G8"); # this will return undef
37             if (!defined($c)) {
38             # couldn't create a check digit
39             }
40              
41             print "Valid LUHN characters are:\n";
42             my %vc = Algorithm::LUHN_XS::valid_chars();
43             for (sort keys %vc) {
44             print "$_ => $vc{$_}\n";
45             }
46              
47             Algorithm::LUHN_XS::valid_chars(map {$_ => ord($_)-ord('A')+10} A..Z);
48             $c = check_digit("A2C4E6G8");
49             print "It worked again\n" if is_valid("A2C4E6G8$c");
50              
51             =head1 DESCRIPTION
52              
53             This module is an XS version of the original Perl Module Algorithm::LUHN, which
54             was written by Tim Ayers. It should work exactly the same, only substantially
55             faster. The supplied check_digit() routine is 100% compatible with the pure
56             Perl Algorithm::LUHN module, while the faster check_digit_fast() and really fast
57             check_digit_rff() are not.
58              
59             How much faster? Here's a benchmark, running on a 3.4GHz i7-2600:
60              
61             C
62              
63             C
64              
65             C
66              
67             C
68              
69             C
70              
71             So, it's 35x to 53x faster than the original pure Perl module, depending on
72             how much compatibility with the original module you need.
73              
74             The rest of the documentation is mostly a copy of the original docs, with some
75             additions for functions that are new.
76              
77             This module calculates the Modulus 10 Double Add Double checksum, also known as
78             the LUHN Formula. This algorithm is used to verify credit card numbers and
79             Standard & Poor's security identifiers such as CUSIP's and CSIN's.
80              
81             You can find plenty of information about the algorithm by searching the web for
82             "modulus 10 double add double".
83              
84             =head1 FUNCTION
85              
86             =over 4
87              
88             =cut
89              
90             =item is_valid CHECKSUMMED_NUM
91              
92             This function takes a credit-card number and returns true if
93             the number passes the LUHN check.
94              
95             Ie it returns true if the final character of CHECKSUMMED_NUM is the
96             correct checksum for the rest of the number and false if not. Obviously the
97             final character does not factor into the checksum calculation. False will also
98             be returned if NUM contains in an invalid character as defined by
99             valid_chars(). If NUM is not valid, $Algorithm::LUHN_XS::ERROR will contain the
100             reason.
101              
102             This function is equivalent to
103              
104             substr $N,length($N)-1 eq check_digit(substr $N,0,length($N)-1)
105              
106             For example, C<4242 4242 4242 4242> is a valid Visa card number,
107             that is provided for test purposes. The final digit is '2',
108             which is the right check digit. If you change it to a '3', it's not
109             a valid card number. Ie:
110              
111             is_valid('4242424242424242'); # true
112             is_valid('4242424242424243'); # false
113              
114             =cut
115              
116             =item is_valid_fast CHECKSUMMED_NUM
117             =cut
118             =item is_valid_rff CHECKSUMMED_NUM
119              
120             As with check_digit(), we have 3 versions of is_valid(), each one progressively
121             faster than the check_digit() that comes in the original pure Perl
122             Algorithm::LUHN module. Here's a benchmark of 1M total calls to is_valid():
123              
124             C
125              
126             C
127              
128             C
129              
130             C
131              
132             C
133              
134             Algorithm::LUHN_XS varies from 38x to 48x times faster than the original
135             pure perl Algorithm::LUHN module. The is_valid() routine is 100% compatible
136             with the original, returning either '1' for success or the empty string ''
137             for failure. The is_valid_fast() routine returns 1 for success and 0 for
138             failure. Finally, the is_valid_rff() function also returns 1 for success
139             and 0 for failure, but only works with numeric input. If you supply any
140             alpha characters, it will return 0.
141              
142             =cut
143              
144             # is_valid is an XS function
145              
146             =item check_digit NUM
147              
148             This function returns the checksum of the given number. If it cannot calculate
149             the check_digit it will return undef and set $Algorithm::LUHN_XS::ERROR to
150             contain the reason why. This is much faster than the check_digit routine
151             in the pure perl Algorithm::LUHN module, but only about half as fast as
152             the check_digit_fast() function in this module, due to the need to return both
153             integers and undef, which isn't fast with XS.
154              
155             =cut
156              
157             =item check_digit_fast NUM
158              
159             This function returns the checksum of the given number. If it cannot calculate
160             the check digit it will return -1 and set $Algorithm::LUHN_XS::ERROR to
161             contain the reason why. It's about 20% faster than check_digit() because the XS
162             code in this case only has to return integers.
163              
164             =cut
165              
166             =item check_digit_rff NUM
167              
168             This function returns the checksum of the given number.
169              
170             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
171             it input with alpha characters, it will return -1. Works great for Credit
172             Cards, but not for things like L.
173              
174             =cut
175              
176             # check_digit, check_digit_fast, and check_digit_rff are XS defined functions
177              
178             =item valid_chars LIST
179              
180             By default this module only recognizes 0..9 as valid characters, but sometimes
181             you want to consider other characters as valid, e.g. Standard & Poor's
182             identifers may contain 0..9, A..Z, @, #, *. This function allows you to add
183             additional characters to the accepted list.
184              
185             LIST is a mapping of C =E C.
186             For example, Standard & Poor's maps A..Z to 10..35
187             so the LIST to add these valid characters would be (A, 10, B, 11, C, 12, ...)
188              
189             Please note that this I or I characters, so any characters
190             already considered valid but not in LIST will remain valid.
191              
192             If you do not provide LIST,
193             this function returns the current valid character map.
194              
195             Note that the check_digit_rff() and is_valid_rff() functions do not support
196             the valid_chars() function. Both only support numeric inputs, and map them
197             to their literal values.
198              
199             =cut
200              
201             sub valid_chars {
202 12 100   12 1 1729 return %map unless @_;
203 8         24 while (@_) {
204 359         564 my ($k, $v) = splice @_, 0, 2;
205 359         811 $map{$k} = $v;
206             }
207 8         139 _al_init_vc(\%map);
208             }
209              
210              
211             sub _dump_map {
212 1     1   24 my %foo = valid_chars();
213 1         14 my ($k,$v);
214 1         1579 print "$k => $v\n" while (($k, $v) = each %foo);
215 1         32 return 1;
216             }
217              
218             =back
219              
220             =cut
221              
222             __END__