C - Mathematics - Using the Luhn algorithm

Maybe you wonder if your credit card has a correct number?

How to know that?
With the Luhn formula!
This is what we are going to see in this Luhn tutorial.

This algorithm can say if it is a good or a bad credit card number.

Indeed, for that we first of all have to reverse the complete number.

For example, the number to test is 1234567897.

Once reversed, we have 7987654321.

We can now add the double of pair series of this number.
In our case, we are going to double the number in bold: 7987654321.

So we have now something like that:

7 18 8 14 6 10 4 6 2 2.

But now we need to add digits of each number:

7 + (1+8) + 8 + (1+4) + 6 + (1+0) + 4 + 6 + 2 + 2.

Finally the result is: 50.

To finish this Luhn test, we have to check if this final result can be divided by 10 without any rest.

So:

50 % 10 == 0

Our number is then validate by the Luhn algorithm.

Let's see this in details with the code below:

Using the Luhn algorithm

main.c

/* main.c */

#include "h.h"

int     main(int ac, char *av[])
{
    t_luhn b;

    init(ac, av, &b);
    return 0;
}

misc.c

/* misc.c  */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include "h.h"

void init(int ac, char *av[], t_luhn *b)
{
    if (ac < 2)
    {
        printf("Usage: ./luhn-algo [number]\n");
        exit(1);
    }
    check_number(av[1]);
    b->classic = av[1];
    b->result = 0;
    b->len = strlen(b->classic);
    check_size(b);
    reverse_str(b);
    calculate_result(b);
    debug(b);
    if (b->rev != NULL)
        free(b->rev);
    else
    {
        fprintf(stderr, "Error: %s\n", strerror(errno));
        exit(1);
    }
}

void find_luhn(t_luhn *b)
{
    if (b->result % 10 == 0)
        printf("GOOD: the code %s is a valid Luhn number!\n", b->classic);
    else
        printf("BAD: the code %s is not a valid Luhn number!\n", b->classic);
}

void check_size(t_luhn *b)
{
    if (b->len < 16)
        b->try = 1;
    else
        b->try = 0;
}

void debug(t_luhn *b)
{

    printf("\n===== START =====\n\n");
    printf("Result = %d\n", b->result);
    find_luhn(b);
    printf("\n===== END =====\n\n");
}

int is_pair(int i)
{
    if (i % 2 == 0)
        return 0;
    return 1;
}

void reverse_str(t_luhn *b)
{
    unsigned int i;

    b->rev = malloc(sizeof(*b->rev) * b->len);
    if (b->rev == NULL)
    {
        fprintf(stderr, "Error: %s\n", strerror(errno));
        exit(1);
    }
    i = 0;
    while (i < b->len)
    {
        b->rev[i] = b->classic[b->len - i - 1];
        ++i;
    }
}

void calculate_result(t_luhn *b)
{
    unsigned int i;
    int newVal[10] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9};

    i = 0;
    while (i < b->len)
    {
        if (is_pair(i))
            b->result += newVal[b->rev[i] - 48];
        else
            b->result += b->rev[i] - 48;
        ++i;
    }
}

void check_number(char *av)
{
    unsigned int i;

    i = 0;
    if (av[0] == '-')
    {
        i = 1;
    }
    while (i < strlen(av))
    {
        if (av[i] < 48 || av[i] > 57)
        {
            fprintf(stderr, "Error: %s\n", "Allowed characters: [0-9]");
            exit(1);
        }
        ++i;
    }
}

h.h

#ifndef H_H_
#define H_H_

/**
 * Structure
 */
typedef struct luhn
{
    char        *classic;
    char        *rev;
    int        result;
    unsigned    len;
    int        try;
} t_luhn;

/**
 * Prototype
 */
void init(int ac, char *av[], t_luhn *b);
void debug(t_luhn *b);
void calculate_result(t_luhn *b);
void check_number(char *av);
void reverse_str(t_luhn *b);
void check_size(t_luhn *b);
void find_luhn(t_luhn *b);

#endif /* H_H_ */

Compiling

gcc main.c misc.c -o luhn-algo ; ./luhn-algo 1234567897

Output

===== START =====

Result = 50
GOOD: the code 1234567897 is a valid Luhn number!

===== END =====

Well done! laugh

Comments

Comment: 

I fail to see any reason why you need to reverse the order of the input data. The final result is the sum of all input data, so if you were to reverse the result of the "is_pair" function you could skip the reverse step and use the input string directly.
There is also a typo in the fprintf of "check_number" you check for numbers between 0 and 9 and your message says "Allowed characters: [1-9]"

Comment: 

Hello Mike,

Thank you for your comment. cool

There was a typo indeed, I fixed it, thanks.

I've to check why I used the reverse_str() function.
I'll do that as soon as possible.

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.