The weekly challenge 290 - Task 2: Luhn’s Algorithm

  1 #!/usr/bin/env perl
  2 # https://theweeklychallenge.org/blog/perl-weekly-challenge-290/#TASK2
  3 #
  4 # Task 2: Luhn’s Algorithm
  5 # ========================
  6 #
  7 # You are given a string $str containing digits (and possibly other characters
  8 # which can be ignored). The last digit is the payload; consider it separately.
  9 # Counting from the right, double the value of the first, third, etc. of the
 10 # remaining digits.
 11 #
 12 # For each value now greater than 9, sum its digits.
 13 #
 14 # The correct check digit is that which, added to the sum of all values, would
 15 # bring the total mod 10 to zero.
 16 #
 17 # Return true if and only if the payload is equal to the correct check digit.
 18 #
 19 # It was originally posted on reddit.
 20 #
 21 ## Example 1
 22 ##
 23 ## Input: "17893729974"
 24 ## Output: true
 25 ##
 26 ## Payload is 4.
 27 ##
 28 ## Digits from the right:
 29 ##
 30 ## 7 * 2 = 14, sum = 5
 31 ## 9 = 9
 32 ## 9 * 2 = 18, sum = 9
 33 ## 2 = 2
 34 ## 7 * 2 = 14, sum = 5
 35 ## 3 = 3
 36 ## 9 * 2 = 18, sum = 9
 37 ## 8 = 8
 38 ## 7 * 2 = 14, sum = 5
 39 ## 1 = 1
 40 ##
 41 ## Sum of all values = 56, so 4 must be added to bring the total mod 10 to
 42 ## zero. The payload is indeed 4.
 43 #
 44 ## Example 2
 45 ##
 46 ## Input: "4137 8947 1175 5904"
 47 ## Output: true
 48 #
 49 ## Example 3
 50 ##
 51 ## Input: "4137 8974 1175 5904"
 52 ## Output: false
 53 #
 54 ############################################################
 55 ##
 56 ## discussion
 57 ##
 58 ############################################################
 59 #
 60 # First, we split the string into its characters, keep only
 61 # the digits and reverse their order. We now put the first
 62 # element into a $payload variable, the rest goes into an
 63 # array @digits. Next, for the odd elements of the array, we
 64 # double the value and sum the digits of that if bigger than 9.
 65 # In the end, we add that to the sum so far. We then calculate
 66 # the sum mod 10, substract that from 10 to find the correct
 67 # payload. If the payload matches that, we return True, otherwise
 68 # False.
 69 
 70 use strict;
 71 use warnings;
 72 
 73 luhns_algorithm("17893729974");
 74 luhns_algorithm("4137 8947 1175 5904");
 75 luhns_algorithm("4137 8974 1175 5904");
 76 
 77 sub luhns_algorithm {
 78    my $str = shift;
 79    print "Input: $str\n";
 80    my ($payload, @digits) = reverse grep { /[0-9]/ } split //, $str;
 81    my $odd = 1;
 82    my $sum = 0;
 83    foreach my $digit (@digits) {
 84       if($odd) {
 85          $digit *= 2;
 86          if($digit > 9) {
 87             my ($first, $second) = split //, $digit;
 88             $digit = $first + $second;
 89          }
 90          $odd = 0;
 91       } else {
 92          $odd = 1;
 93       }
 94       $sum += $digit;
 95    }
 96    my $mod = $sum % 10;
 97    my $correct = 10 - $mod;
 98    if($payload == $correct) {
 99       print "Output: True\n";
100    } else {
101       print "Output: False\n";
102    }
103 }