File Coverage

blib/lib/Algorithm/CheckDigits/M10_001.pm
Criterion Covered Total %
statement 57 66 86.3
branch 17 26 65.3
condition 1 3 33.3
subroutine 12 12 100.0
pod 4 5 80.0
total 91 112 81.2


line stmt bran cond sub pod time code
1             # vim: set ts=4 sw=4 tw=78 si et:
2             package Algorithm::CheckDigits::M10_001;
3              
4 2     2   37 use 5.006;
  2         7  
5 2     2   10 use strict;
  2         15  
  2         43  
6 2     2   10 use warnings;
  2         5  
  2         59  
7 2     2   519 use integer;
  2         16  
  2         10  
8              
9 2     2   53 use version; our $VERSION = qv('v1.3.6');
  2         4  
  2         8  
10              
11             our @ISA = qw(Algorithm::CheckDigits);
12              
13             my %prefix = (
14             'amex' => [ '34', '37', ],
15             'bahncard' => [ '70', ],
16             'diners' => [ '30[0-5]', '36', '38', ],
17             'discover' => [ '6011', ],
18             'enroute' => [ '2014', '2149', ],
19             'jcb' => [ '1800', '2131', '3088', ],
20             'mastercard' => [ '5[1-5]', ],
21             'miles&more' => [ '99', '22', ],
22             'visa' => [ '4', ],
23             );
24              
25             my %ctable = (
26             '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4,
27             '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
28             'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14,
29             'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19,
30             'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, 'O' => 24,
31             'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29,
32             'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34,
33             'Z' => 35,
34             );
35              
36             # Aliases
37             $prefix{'eurocard'} = $prefix{'mastercard'};
38              
39             # omit prefixes doesn't work with the test numbers
40             my %omitprefix = (
41             'jcb' => 0,
42             'enroute' => 0,
43             'discover' => 0,
44             );
45              
46             sub new {
47 16     16 0 27 my $proto = shift;
48 16         24 my $type = shift;
49 16   33     59 my $class = ref($proto) || $proto;
50 16         34 my $self = bless( {}, $class );
51 16         52 $self->{type} = lc($type);
52 16         42 $self->_determine_pattern();
53 16         66 return $self;
54             } # new()
55              
56             sub is_valid {
57 33     33 1 109 my ( $self, $number ) = @_;
58 33 100       344 if ( $number =~ /^($self->{pattern})([0-9])$/i ) {
59 31         136 return $2 == $self->_compute_checkdigit( uc($1) );
60             }
61 2         9 return '';
62             } # is_valid()
63              
64             sub complete {
65 14     14 1 1118 my ( $self, $number ) = @_;
66 14 50       169 if ( $number =~ /^$self->{pattern}$/i ) {
67 14         47 return $number . $self->_compute_checkdigit( uc($number) );
68             }
69 0         0 return '';
70             } # complete()
71              
72             sub basenumber {
73 14     14 1 38 my ( $self, $number ) = @_;
74 14 50       157 if ( $number =~ /^($self->{pattern})([0-9])$/i ) {
75 14 50       56 return $1 if ( $2 == $self->_compute_checkdigit( uc($1) ) );
76             }
77 0         0 return '';
78             } # basenumber()
79              
80             sub checkdigit {
81 14     14 1 34 my ( $self, $number ) = @_;
82 14 50       154 if ( $number =~ /^($self->{pattern})([0-9])$/i ) {
83 14 50       52 return $2 if ( $2 == $self->_compute_checkdigit( uc($1) ) );
84             }
85 0         0 return '';
86             } # checkdigit()
87              
88             sub _compute_checkdigit {
89 73     73   123 my $self = shift;
90 73         134 my $number = shift;
91 73         249 $number =~ s/\s//g;
92 73 50       178 if ( $omitprefix{ $self->{type} } ) {
93 0         0 my $pf = $prefix{ $self->{type} };
94 0         0 for my $p ( @{$pf} ) {
  0         0  
95 0 0       0 if ( $number =~ /^$p([0-9]+)$/ ) {
96 0         0 $number = $1;
97 0         0 last;
98             }
99             }
100             }
101 73 100       214 if ('isin' eq $self->{type}) {
    100          
102             # With ISIN letters are handled differently than for instance with
103             # CUSIP, so we substitute them here
104 5         19 $number =~ s/([A-Z])/$ctable{$1}/ge;
  10         34  
105             }
106             elsif ('imeisv' eq $self->{type}) {
107             # With IMEISV the SV (software version) is left out from the
108             # computation of the checkdigit
109 2 50       8 $number = substr( $number, 0, 14 ) if ( 'imeisv' eq $self->{type} );
110             }
111              
112 73         269 my @digits = map { $ctable{$_} } split( //, $number );
  891         1389  
113 73         164 my $even = 1;
114 73         101 my $sum = 0;
115 73         192 for ( my $i = $#digits; $i >= 0; $i-- ) {
116 891 100       1337 if ($even) {
117 463         657 my $tmp = 2 * $digits[$i];
118 463         695 $sum += $tmp / 10 + $tmp % 10;
119             }
120             else {
121 428         672 $sum += $digits[$i] / 10 + $digits[$i] % 10;
122             }
123 891         1709 $even = not $even;
124             }
125 73         465 return ( 10 - $sum % 10 ) % 10;
126             } # _compute_checkdigit()
127              
128             sub _determine_pattern {
129 16     16   23 my $self = shift;
130 16 100       39 if ('cusip' eq $self->{type}) {
131 5         17 $self->{pattern} = qr/[0-9A-Z]{8}/io;
132             }
133             else {
134 11         44 $self->{pattern} = qr/[0-9A-Z ]+/io;
135             }
136             } # _determine_pattern()
137              
138             # Preloaded methods go here.
139              
140             1;
141             __END__