ccs50luhn

Unusual behavior with Luhn's algorithm in C


I've been doing a CS50 course and stumbled upon this "Credit" problem. At the moment I'm testing it and due to insufficient cases provided in worksheet, I went to the recommended PayPal testing card numbers: https://developer.paypal.com/api/nvp-soap/payflow/integration-guide/test-transactions/#standard-test-cards Particularly:

These refuse to cooperate. My program calculates the Luhn's value for them, and it's not close to being a multiplier of 10; am I missing something, because other providers are working perfectly fine.

Results from code check:

:( identifies 5555555555554444 as MASTERCARD
    expected "MASTERCARD\n", not "114\nINVALID\n..."
:( identifies 5105105105105100 as MASTERCARD
    expected "MASTERCARD\n", not "47\nINVALID\n"

My code:

#include <stdio.h>
#include <cs50.h>

int get_sum_from_second_to_last(long temp_number);

int get_sum_from_odd_digits(long temp_number);

int length(long temp_number);

int return_first_n_digits(int n, long temp_number, int length);

int main()
{
    int final_sum = 0;
    long number = 0;
    do
    {
        number = get_long("Number: ");
    }
    while(number<0);
    final_sum = get_sum_from_second_to_last(number) + get_sum_from_odd_digits(number);
    printf("%i\n", final_sum);
    if(final_sum % 10 == 0)
    {
        if(length(number) == 15 && (return_first_n_digits(2, number, length(number)) == 34 || return_first_n_digits(2, number, length(number)) == 37))
        {
            printf("AMEX\n");
        }
        else
        {
            if((length(number) == 16 || length(number) == 13) && return_first_n_digits(1, number, length(number)) == 4)
            {
                printf("VISA\n");
            }
            else
            {
                if(length(number) == 16 && (return_first_n_digits(2, number, length(number)) == 51 || return_first_n_digits(2, number, length(number)) == 52 || return_first_n_digits(2, number, length(number)) == 53 || return_first_n_digits(2, number, length(number)) == 54 || return_first_n_digits(2, number, length(number)) == 55))
                {
                    printf("MASTERCARD\n");
                }
                else
                {
                    printf("This card provider recognition is not supported\n");
                }
            }
        }
    }
    else
    {
        printf("INVALID\n");
    }
}

int get_sum_from_second_to_last(long temp_number)
{
    int digit_current = 0;
    int counter = 1;
    int sum = 0;
    do
    {
        digit_current = temp_number % 10;
        if(counter%2 == 0)
        {
            if((digit_current*2)%10!=0)
            {
                sum = sum + (digit_current*2)%10 + (digit_current*2)/10;
            }
            else
            {
                sum = sum + digit_current*2;
            }
        }
        temp_number = temp_number/10;
        counter += 1;
    }
    while(temp_number);
    return sum;
}

int get_sum_from_odd_digits(long temp_number)
{
    int digit_current = 0;
    int counter = 1;
    int sum = 0;
    do
    {
        digit_current = temp_number % 10;
        if(counter%2 != 0)
        {
            sum = sum + digit_current;
        }
        temp_number = temp_number/10;
        counter += 1;
    }
    while(temp_number);
    return sum;
}

int length(long temp_number)
{
    int counter = 0;
    do
    {
        temp_number = temp_number/10;
        counter++;
    }
    while(temp_number);
    return counter;
}

int return_first_n_digits(int n, long temp_number, int length)
{
    int i;
    for(i = 0; i < length - n; i++)
    {
        temp_number = temp_number/10;
    }
    return temp_number;
}

Solution

  • The issue appears to be in your function for summing up the even numbered digits in the int get_sum_from_second_to_last function. In that function, the digits are multiplied by a factor of two. Then according to information on how the Luhn's algorithm is supposed to work, if the result is a two-digit number, those digits are then supposed to be added together to derive a single digit. It appears that doesn't always happen with the current testing within that function. Since the value of multiplying one digit by "2" can only result in two-digit numbers from "10" to "18", one can effectively derive the summation of the digits by just subtracting the value of "9" from the calculated result.

    With that, I offer up the following code snippet as an alternative summation of the even-numbered digits.

    int get_sum_from_second_to_last(long temp_number)
    {
        int digit_current = 0;
        int counter = 1;
        int sum = 0;
        do
        {
            digit_current = temp_number % 10;
            if(counter%2 == 0)
            {
                if((digit_current*2) > 9)
                {
                    sum = sum + (digit_current * 2 - 9);
                }
                else
                {
                    sum = sum + digit_current * 2;
                }
            }
            temp_number = temp_number/10;
            counter += 1;
        }
        while(temp_number);
    
        printf("Even digit sum: %d\n", sum);
    
        return sum;
    }
    

    FYI, you can leave out the printf() call. I added that just for some visual clarification.

    With that change, I tested out the four sample numbers in your narrative and they all produced a valid MASTERCARD result.

    Number: 5299999999999990
    Even digit sum: 64
    Current digit: 0
    Current digit: 9
    Current digit: 9
    Current digit: 9
    Current digit: 9
    Current digit: 9
    Current digit: 9
    Current digit: 2
    Odd digit sum: 56
    Final sum: 120
    MASTERCARD
    

    As a further test, I actually tested out a number for AMEX and for VISA and those worked as well.

    I hope that clarifies things.

    Regards.