package LUHN;
use warnings;
use strict;
use List::Util qw(sum);
use Data::Dump qw(dd);
sub cd_kschwab { # pm#1226545
use integer;
my $total = 0;
my $flip = 1;
foreach my $c (reverse split //, shift) {
$c *= 2 unless $flip = !$flip;
while ($c) {
$total += $c % 10;
$c = $c / 10;
}
}
return (10 - $total % 10) % 10;
}
my @x2_cast_out_9 = (
# 0 1 2 3 4 5 6 7 8 9 # input number n
0, 2, 4, 6, 8, 1, 3, 5, 7, 9 # output: n * 2, cast out 9
);
my @check_digit = map { (10 - $_ % 10) % 10 } 0 .. 9 * 15;
sub cd_AnomalousMonk {
my $ccn = shift;
my $total = 0;
for (1, 3, 5, 7, 9, 11, 13) {
$total += substr($ccn, $_, 1);
}
for (0, 2, 4, 6, 8, 10, 12, 14) {
$total += $x2_cast_out_9[ substr($ccn, $_, 1) ];
}
return $check_digit[ $total ];
}
sub cd_BrowserUk { # pm#1226582
use integer;
my $s = $_[ 0 ];
my $total = 0;
for my $i ( 0 .. 14 ) {
my $d = substr( $s, $i, 1 );
unless( $i & 1 ) {
$d *= 2;
$d -= 9 if $d > 9;
}
$total += $d;
}
$total *= 9;
return chop $total;
}
1;
####
# test_LUHN_1.pl 01dec18waw
use warnings;
use strict;
use Benchmark qw(cmpthese);
use LUHN;
for ('000' .. '999') {
my $ccn = '401135000000' . $_;
my $kschwab_cd = LUHN::cd_kschwab ($ccn);
my $AnomalousMonk_cd = LUHN::cd_AnomalousMonk ($ccn);
die "$ccn: kschwab $kschwab_cd != $AnomalousMonk_cd"
if $kschwab_cd != $AnomalousMonk_cd;
}
print "tested cc number range kschwab vs. AnomalousMonk ok \n";
for ('000' .. '999') {
my $ccn = '401135000000' . $_;
my $kschwab_cd = LUHN::cd_kschwab ($ccn);
my $BrowserUk_cd = LUHN::cd_BrowserUk ($ccn);
die "$ccn: kschwab $kschwab_cd != $BrowserUk_cd"
if $kschwab_cd != $BrowserUk_cd;
}
print "tested cc number range kschwab vs. BrowserUk ok \n";
cmpthese (-1, {
cd_kschwab => sub { LUHN::cd_kschwab ('401135000000000') },
cd_AM => sub { LUHN::cd_AnomalousMonk ('401135000000000') },
cd_BrowserUk => sub { LUHN::cd_BrowserUk ('401135000000000') },
});
##
##
c:\@Work\Perl\monks\kschwab>perl test_LUHN_1.pl
tested cc number range kschwab vs. AnomalousMonk ok
tested cc number range kschwab vs. BrowserUk ok
Rate cd_kschwab cd_BrowserUk cd_AM
cd_kschwab 52764/s -- -40% -62%
cd_BrowserUk 88033/s 67% -- -37%
cd_AM 139999/s 165% 59% --
##
##
sub cd_AM_HOP { # iterator solution
my ($n_digits, # n of digits for which to generate luhn checksum
) = @_;
my $order = $n_digits & 1;
my @straight = grep +($_ & $order), 0 .. $n_digits-1; # dd '---', \@straight;
my @adjusted = grep !($_ & $order), 0 .. $n_digits-1; # dd '===', \@adjusted;
my @check_digit = map { (10 - $_ % 10) % 10 } 0 .. 9 * $n_digits;
return sub {
my $ccn = shift; # digits for which to generate checksum
my $total = 0;
for (@straight) {
$total += substr($ccn, $_, 1);
}
for (@adjusted) {
$total += $x2_cast_out_9[ substr($ccn, $_, 1) ];
}
# return $check_digit[ $total ]; # return check digit
$total *= 9;
return chop $total; # return check digit
}; # end sub iterator
}