Peter
Peter Campbell Smith

Double existence and
checking the payload

Weekly challenge 290 — 7 October 2024

Week 290: 7 Oct 2024

Task 2

Task — Luhn’s algorithm

You are given a string $str containing digits (and possibly other characters which can be ignored). The last digit is the payload; consider it separately.

Counting from the right, double the value of the first, third, etc of the remaining digits. For each value now greater than 9, sum its digits.

The correct check digit is that which, added to the sum of all values, would bring the total mod 10 to zero. Return true if and only if the payload is equal to the correct check digit.

Examples


Example 1
Input: "17893729974"
Output: true
Payload is 4.
Digits from the right:
7 * 2 = 14, sum = 5
9 = 9
9 * 2 = 18, sum = 9
2 = 2
7 * 2 = 14, sum = 5
3 = 3
9 * 2 = 18, sum = 9
8 = 8
7 * 2 = 14, sum = 5
1 = 1
Sum of all values = 56, so 4 must be added to bring the 
total mod 10 to zero. The payload is indeed 4.

Example 2
Input: "4137 8947 1175 5904"
Output: true

Example 3
Input: "4137 8974 1175 5904"
Output: false

Analysis

The Luhn algorithm is widely used as a simple check that a string of digits has been entered correctly, It will catch many, though not all, missing or transposed digits.

The implementation is easy, essentially following the steps in the definition above, ie:

  • Delete any non digits
  • Take off the last, payload digit
  • Loop back from the end of the remaining string, summing the value of even numbered digits and mod 10 double the value of the odd numbered ones
  • The check digit is the number (0 - 9) that needs to be added to the sum to make it a multiple of 10
  • The result is true if the check digit equals the payload

Credit card numbers use the Luhn algorithm for an initial check, I suggest that you don't type your own credit card number below, but here are some dummy numbers that the issuers offer for testing purposes:

Visa: 4111 1111 1111 1111 or 4242 4242 4242 4242
MasterCard: 5431 1111 1111 1111
Amex: 3711 1111 1111 114
Diners: 3600 0000 0000 08

Try it 

Try running the script with any input:



example: 4111 1111 1111 1111

Script


#!/usr/bin/perl

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

use v5.26;    # The Weekly Challenge - 2024-10-07
use utf8;     # Week 290 - task 2 - Luhn’s algorithm
use warnings; # Peter Campbell Smith
binmode STDOUT, ':utf8';

luhns_algorithm('17893729974');
luhns_algorithm('4137 8947 1175 5904');
luhns_algorithm('4137 8974 1175 5904');
luhns_algorithm('3141 5926 5358 9796');

sub luhns_algorithm {
    
    my ($str, $data, $payload, $check_sum, $digit, $check_digit, $odd, $j);
    
    # get rid of non-digits
    $str = $_[0];
    $str =~ s|[^\d]||g;
    
    # strip payload
    ($data, $payload) = $str =~ m|(\d+)(\d)|;
    
    # loop over digits from right to left
    $check_sum = 0;
    $odd = 1;
    for ($j = length($data) - 1; $j >= 0; $j --) {
        $digit = substr($data, $j, 1);
        
        # if an odd digit double and mod 10
        if ($odd) {
            $digit *= 2;
            $digit -= 9 if $digit >= 10;
        }
        $check_sum += $digit;
        $odd = 1 - $odd;
    }
    
    # get check digit
    $check_digit = 10 - (($check_sum - 1) % 10 + 1);
        
    say qq[\nInput:  \@str = '$_[0]'];
    say qq[Output: ] . ($check_digit == $payload ? 'true' : 'false') . qq[ (payload = $payload, check_digit = $check_digit)];
}

Output


Input:  @str = '17893729974'
Output: true (payload = 4, check_digit = 4)

Input:  @str = '4137 8947 1175 5904'
Output: true (payload = 4, check_digit = 4)

Input:  @str = '4137 8974 1175 5904'
Output: false (payload = 4, check_digit = 0)

Input:  @str = '3141 5926 5358 9796'
Output: true (payload = 6, check_digit = 6)

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain