Detect valid credit card number algorithm - c

I just enrolled in the online CS50 course and doing the pset1 about detecting valid credit card number. However, my algorithm does not work well as I expected. I used debugging tool to see step by step result, as long as variable i run from 1 to 9 it works perfectly, all values are added to the sum correctly. But when it comes to i = 10 and so on, the numNotSquared got assigned -8 and numSquared got assigned -16 and it keeps like that until the end of the number. Please help me with that, thank you.
// Headers and libraries
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main(void)
{
// Prompt user for card number
long cardNumber = get_long("Enter card number: ");
// Get the length of input
int length = floor(log10(cardNumber)) + 1;
// Range validation
if (length < 13 || length > 16)
{
printf("INVALID\n");
}
int numSquared = 0;
int numNotSquared = 0;
int sum = 0;
// Algorithm to detect valid card
// Based on Luhn's algorithm (https://lab.cs50.io/cs50/labs/2020/x/credit/)
for (int i = 1; i <= length; i++)
{
// If digit is on odd position then mutiply by two
if (i % 2 != 0)
{
{
numSquared = ((int)(cardNumber / pow(10, length - i)) % 10) * 2;
}
// If the total is >= 10, then sum the products' digit
if (numSquared >= 10)
{
sum += ((numSquared % 10) + 1);
}
else
{
sum += numSquared;
}
}
// If digit is on even position then add to the sum
else
{
numNotSquared = (int)(cardNumber / pow(10, length - i)) % 10;
sum += numNotSquared;
}
}
// Find remainder of (total / 10)
if (sum % 10 == 0)
{
if (floor(cardNumber / pow(10, length - 1)) == 4)
{
printf("VISA\n");
}
else
{
int firstTwoDigits = floor(cardNumber / pow(10, length - 2));
if (firstTwoDigits == 34 || firstTwoDigits == 37)
{
printf("AMEX\n");
}
else if (firstTwoDigits == 51 || firstTwoDigits == 52 || firstTwoDigits == 53 || firstTwoDigits == 54 || firstTwoDigits == 55)
{
printf("MASTERCARD\n");
}
}
}
else
{
printf("INVALID\n");
}
}

Related

My CS50 Credit code is not producing any output after adding a check for type of credit card

The calculating if the card is valid is correct as I tested that and the code performed correctly there, so I removed that section from under the Do section loop to appease stackoverflow. The problem comes from deciding what type of card was used; the program does nothing and waits for the next command in the console. I'm just not seeing what the issue is. Below is my code and I tested with card number 4003600000000014, the result should've printed out "Visa":
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main(void)
{
// Array to store the individual digits of the card
long card, card_copy;
int pos = 0, sum = 0, card_id;
do
{
card = get_long("CC#: ");
card_copy = card;
while(card != 0)
{
if(pos % 2 != 0)
{
int temp = 2 * (card % 10);
// For #s > 9
if(temp > 9)
{
sum += (temp % 10 + temp / 10);
}
else
{
sum += temp;
}
}
else
{
sum += card % 10;
}
card /= 10; // Divides card # to remove the decimal place
// and moves to the next digit in CC
pos++;
}
}
while(card != 0);
if(sum % 10 == 0)
{
// Divides card to only have the first 2 digits
card_id = card_copy / (pow(10, pos - 1));
// Divides card to only have first digit; 4 for Visa
int visa_id = card_copy / (pow(10, pos - 2));
// 16-digit; start with 51, 52, 53, 54, or 55
if(pos == 15 && (card_id >= 51 && card_id <= 55))
{
printf("MasterCard");
}
// 15-digit; start with 34 or 37
else if(pos == 14 && (card_id == 34 || card_id == 37))
{
printf("American Express");
}
// 13-digit; start with 4
else if(pos == 12 && visa_id == 4)
{
printf("Visa");
}
// 16-digit; start with 4
else if(pos == 15 && visa_id == 4)
{
printf("Visa");
}
}
else
{
printf("Invalid: %d\n", sum);
}
}
The long type has a maximum value of 2147483647. This wouldn't hold the shortest card type I know which is 12 digits.
Either change to unsigned long long or better still use a string.

CS50 Credit Card Validation

#include <stdio.h>
#include <cs50.h>
int main(void)
{
long cc = get_long("Credit Card: "); // gets input
long len = 0; //intialized length
long x = cc; // set 2nd variable = to cc to prevent manipulation of cc
while (x != 0) // length count loop while x is divisable loop will continue will be stored as len
{
x = x / 10;
len++;
}
if ((len != 16) && (len != 15) && (len != 13)) //Checking for length to see if number matchs possible postive outcomes
{
printf("INVALID\n");
return 0;
}
//pull 2nd to last and then every other digit
long cc_num1 = ((cc % 100) / 10);
long cc_num2 = ((cc % 10000) / 1000);
long cc_num3 = ((cc % 1000000) / (100000));
long cc_num4 = ((cc % 100000000) / (10000000));
long cc_num5 = ((cc % 10000000000) / (1000000000));
long cc_num6 = ((cc % 1000000000000) / (100000000000));
long cc_num7 = ((cc % 100000000000000) / (10000000000000));
long cc_num8 = ((cc % 10000000000000000) / (1000000000000000));
cc_num1 = (cc_num1 * 2); //Multiply digits pulled above by 2
cc_num2 = (cc_num2 * 2);
cc_num3 = (cc_num3 * 2);
cc_num4 = (cc_num4 * 2);
cc_num5 = (cc_num5 * 2);
cc_num6 = (cc_num6 * 2);
cc_num7 = (cc_num7 * 2);
cc_num8 = (cc_num8 * 2);
cc_num1 = ((cc_num1 / 10) + (cc_num1 % 10)); //split double digits and add to signles
cc_num2 = ((cc_num2 / 10) + (cc_num2 % 10));
cc_num3 = ((cc_num3 / 10) + (cc_num3 % 10));
cc_num4 = ((cc_num4 / 10) + (cc_num4 % 10));
cc_num5 = ((cc_num5 / 10) + (cc_num5 % 10));
cc_num6 = ((cc_num6 / 10) + (cc_num6 % 10));
cc_num7 = ((cc_num7 / 10) + (cc_num7 % 10));
cc_num8 = ((cc_num8 / 10) + (cc_num8 % 10));
long cc_sum = cc_num1 + cc_num2 + cc_num3 + cc_num4 + cc_num5 + cc_num6 + cc_num7 + cc_num8; // add sum of number above
long cc_num1x = ((cc % 10) / 1); //pulls last digit from card then everyother digit
long cc_num2x = ((cc % 1000) / 100);
long cc_num3x = ((cc % 100000) / 10000);
long cc_num4x = ((cc % 10000000) / 1000000);
long cc_num5x = ((cc % 1000000000) / 100000000);
long cc_num6x = ((cc % 100000000000) / 10000000000);
long cc_num7x = ((cc % 10000000000000) / 1000000000000);
long cc_num8x = ((cc % 1000000000000000) / 100000000000000);
long cc_sumx = cc_num1x + cc_num2x + cc_num3x + cc_num4x + cc_num5x + cc_num6x + cc_num7x +
cc_num8x; //adds last and everyother digit together
long sumofsums = cc_sum + cc_sumx; // adds sums of both sums created
if ((sumofsums % 10) != 0) // Luhn’s Algorithm results will close if not met
{
printf("INVALID\n");
return 0;
}
{
if (len == 15) // checks for AMEX by using length then first 2 digits
{
long ax = cc / 10000000000000;
if ((ax == 34 || ax == 37))
{
printf("AMEX\n");
}
else
{
printf("INVALID\n");
return 0;
}
}
}
long mc = cc / 100000000000000;
long v = cc / 1000000000000000;
long v2 = cc / 1000000000000;
if (len == 16) // Checks for MC and Via (16 digits) by length then first 2 digits MC or 1 visa
{
if ((mc == 51 || mc == 52 || mc == 53 || mc == 54 || mc == 55))
{
printf("MASTERCARD\n");
}
else if (v == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
return 0;
}
}
if (len == 13) //Checks 2nd Visa length 13 digits then 1st digit
{
if (v2 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
return 0;
}
}
}
There has to be a better way then the way I am planning to do this. The Length count loop is fine until 10 digits but then pulls random numbers.
The every other digit formula seems like it can be done through recursion but I am blanking on that. Since the number is limited to 16 at most the formula I am using seems to work.
Determine if card is 15 || 16 || 13 digits if not mark In valid in IF Else loop
Use CC check sum formula If else loop (In valid if it doesn't meet Criteria)
Look at 2 Starting numbers to determine AX, MC or Visa
#include <stdio.h>
#include <cs50.h>
#include <string.h>
int main(void)
{
long cc = get_long("Credit Card: " ); // gets input
int len = 0; //intialized length
int x = cc; // set 2nd variable = to cc to prevent manipulation of cc
while(x != 0) // length count loop while x is divisable loop will continue will be stored as len
{
x = x/10;
len++;
}
printf("%i\n", len); // REMOVE !!!!!!!!!!! BUG TEST
//pull 2nd to last and then every other digit
int cc_num1 = ((cc % 100)/10);
int cc_num2 = ((cc % 10000)/1000);
int cc_num3 = ((cc % 1000000)/(100000));
int cc_num4 = ((cc % 100000000)/(10000000));
int cc_num5 = ((cc % 10000000000)/(1000000000));
int cc_num6 = ((cc % 1000000000000)/(100000000000));
int cc_num7 = ((cc % 100000000000000)/(10000000000000));
int cc_num8 = ((cc % 10000000000000000)/(1000000000000000));
printf("%i %i %i %i %i %i %i %i", cc_num1, cc_num2, cc_num3, cc_num4 , cc_num5, cc_num6 , cc_num7 , cc_num8 );
}
Let's acknowledge the elephant in the room first.
long cc = get_long("Credit Card: " );
...
int x = cc;
The C standard specifies long to be at least 32 bits, whereas int must be at least 16 bits. The actual values are dependent on your system and your library implementation of course. But more often than not, long will be capable of storing more bits than an int. As is the case here. This means "numbers with more than 10 digits", essentially numbers that are too large to be stored into an int, will cause undefined behavior. To know exactly which number is the upper limit for int in your system/environment, you may print the value of INT_MAX, defined in limits.h.
The solution is, of course, to store the long variable in another long variable, not an int. Or, simply pass the value to a function that does the necessary work. Putting everything in main isn't being very organized now is it.
How about we make a function that basically prints all the details about a card given the card's number?
The signature will look like-
void print_card_details(long num)
Now we need a function to put the card through luhn's algorithm. We can also make a function for that-
int is_valid(long num)
{
int curr_digit, add_digit, prod_sum = 0, sum = 0;
for (int digit_count = 0; num != 0; num /= 10, digit_count++)
{
// Strip each digit from number, starting from the end
curr_digit = num % 10;
if (digit_count % 2 != 0)
{
// Every 2nd digit from the right goes through this
// The current digit gets doubled
// The digits of that result are added to the sum
add_digit = curr_digit * 2;
prod_sum += add_digit % 10 + add_digit / 10;
}
else
{
// The remaining digits go through this
// They are all summed up
sum += curr_digit;
}
}
if ((prod_sum + sum) % 10 != 0)
{
// If the sum of prod_sum + sum doesn't end in 0
// It is invalid
return 0;
}
else
{
// The card is valid
return 1;
}
}
The conventional way to iterate through the digits of a number is not to bruteforcefully divide arbitrary powers of 10 manually, but to iterate through it and divide and modulus by 10. For example, this snippet-
while (x != 0)
{
printf("Current digit: %d\n", x % 10);
x /= 10;
}
Will print all digits of the number stored in x. This is essentially what we've used in the luhn's algorithm loop. Except we also keep a count of the total digits, because we only want every second digit starting from the end. How do we know the current digit qualifies this criteria? We check if the current digit_count is even (by dividing by 2 and checking the leftover is 0).
The formula that follows-
add_digit = curr_digit * 2;
prod_sum += add_digit % 10 + add_digit / 10;
is basically the implementation of this-
Multiply every other digit by 2, starting with the number’s second-to-last digit, and then add those products’ digits together.
Make sure only the digits of the resulting add_digit is added. So if add_digit ended up being 12. We need to add 1 + 2. That's exactly what add_digit % 10 + add_digit / 10 does. 12 % 10 is, of course, 2. And 12 / 10 is 1.
This function returns 1 if the card is valid, 0 if it's not. You can fit this up in your main function and check the return value to know whether the card is valid.
If it is valid, move on to the next step of checking the number of digits the card has, as well as what it begins with.
We can make a loop to count the number of digits, as well as store the very first and second digit of the number.
int len = 0;
int curr_digit = 0, prev_digit = 0;
while(num != 0)
{
prev_digit = curr_digit;
curr_digit = num % 10;
num /= 10;
len++;
}
This will give you the length of the card number. Notice, in the last iteration, the value of prev_digit is the second digit and the curr_digit is the first. So curr_digit * 10 + prev_digit will yield the first 2 numbers (together) that the credit card number begins with.
Finally, you just need a bunch of simple if/else clauses to verify which card it is. You're only asked to check for a very small subset as well. So here it is-
// Construct the 2 digit number that this card num begins with
int begins_with = curr_digit * 10 + prev_digit;
if (len == 13 && begins_with / 10 == 4)
{
// We know only VISA uses 13 digits
// And it begins with 4 (second digit does not matter)
printf("VISA\n");
}
else if (len == 15 && begins_with == 34 ||)
{
// We know only AMEX uses 15 digits
printf("AMEX\n");
}
else if (len == 16)
{
// Both VISA and MASTERCARD use 16 digits
if (curr_digit == 4)
{
// But VISA card number begins with 4
printf("VISA\n");
}
else if (curr_digit == 5)
{
// MASTERCARD number begins with 5
printf("MASTERCARD\n");
}
else
{
// Out of context for this problem
printf("INVALID\n");
}
}
else
{
// Out of context for this problem
printf("INVALID\n");
}
Put that all together, and you should hopefully get
void print_card_details(long num)
{
if (!is_valid(num))
{
// Card did not pass luhn's algo
printf("INVALID\n");
return;
}
int len = 0;
int curr_digit = 0, prev_digit = 0;
while(num != 0)
{
prev_digit = curr_digit;
curr_digit = num % 10;
num /= 10;
len++;
}
// Construct the 2 digit number that this card num begins with
int begins_with = curr_digit * 10 + prev_digit;
if (len == 13 && curr_digit == 4)
{
// We know only VISA uses 13 digits
// And it begins with 4 (second digit does not matter)
printf("VISA\n");
}
else if (len == 15 && (begins_with == 34 || begins_with == 37))
{
// We know only AMEX uses 15 digits
printf("AMEX\n");
}
else if (len == 16)
{
// Both VISA and MASTERCARD use 16 digits
if (curr_digit == 4)
{
// But VISA card number begins with 4
printf("VISA\n");
}
else if (begins_with >= 51 && begins_with <= 55)
{
// MASTERCARD number begins with 51, 52, 53, 54, or 55
printf("MASTERCARD\n");
}
else
{
// Out of context for this problem
printf("INVALID\n");
}
}
else
{
// Out of context for this problem
printf("INVALID\n");
}
return;
}

CS50 pset 1 credit (long divided by a number)

I am currently trying my best to solve the question. However, I have encountered the following problem:
After checking the sum, I want to verify the first 2 digits from the card no.
so I use the following method:
int main(void)
{
long long ccn;
do
{
ccn = get_long_long("Credit Card No.:\n");
}
while (ccn < 0);
int ccn_len;
long long count = ccn;
long long bccn = ccn;
for (ccn_len = 0; count != 0; ccn_len++, count /= 10);
int sum = 0; //checksum
for (int i = 0; i < ccn_len; ccn /= 10, i++)
{
if (i % 2 == 0)
{
sum += ccn % 10;
}
else
{
int digit = (ccn % 10) * 2;
sum += digit / 10 + digit % 10;
}
}
if (sum % 10 != 0)
{
printf("INVALID");
}
else
{
int a = bccn / 1e13;
if ((bccn / 1e13 == 34 || bccn / 1e13 == 37) && ccn_len == 15)
{
printf("AMERICAN EXPRESS");
}
else if (bccn / 1e12 == 4 && ccn_len == 13)
{
printf("VISA");
}
else if (ccn_len == 16)
{
if (bccn / 1e15 == 4)
{
printf("VISA");
}
if (bccn / 1e14 > 50 || bccn / 1e14 < 56)
{
printf("MASTERCARD");
}
}
else
{
printf("INVALID");
}
}
}
}
Let's say it is a valid AE card: 378282246310005 with a length of 15 digits
In the above code, I use ccn / 1e13 to get the first two digits to check whether it is 34 or 37.
However, after satisfying the checksum, the output still shows INVALID.
I try to use another method,
I set a variable a
and a = ccn / 1e13
and then I put a in the if-statement:
if ((a == 34 || a == 37) || ccn_len == 15)
everything works fine this time.
Can anyone tell me what is going wrong with my code? Or how do I write better?
Your replies are very much appreciated.
1e13 is a floating-point constant with type double. In bccn / 1e13, bccn is converted to double, and then floating-point division is performed, yielding a number such as 37.82822463100050214279690408147871494293212890625. Then bccn / 1e13 == 37 evaluates as false because 37.82822463100050214279690408147871494293212890625 is not equal to 37.
Rewrite your code to use only integer arithmetic (do not use floating-point constants like 1e13) or to treat credit card “numbers” as strings of digits rather than as integers.

CS50 Problem Set 1 (Credit) 2020 help needed

I am trying to prompt the user for a credit card number and determine whether it is a real credit card number or not, and if so what type of credit card number.
I thought I'd finally got it however when doing check50 the following two inputs produce no output:
1234567890
4111111111111113
They should be giving INVALID but I can't figure out why they aren't giving any output.
This is my code:
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main(void)
{
long Card_Number;
int Digit_Number = 0, Current_Digit = 0, Even_x2_Product = 0, Even_Digits = 0, Odd_Digits = 0,
Total_Digit_Sum = 0;
bool is_even = false;
// Prompt User for Credit Card Number
do
{
Card_Number = get_long("Card Number: ");
}
while (Card_Number < 0);
// Check First Digits of Number
int Digits_MstrCrd = Card_Number / pow(10, 14);
int Digits_Visa_16 = Card_Number / pow(10, 15);
int Digits_AmEx = Card_Number / pow(10, 13);
int Digits_Visa_13 = Card_Number / (pow(10, 12));
// Loop to determine identity of each digit
while (Card_Number != 0)
{
// Get Last Digit of Number
Current_Digit = (Card_Number % 10);
// Increase Digit Number by 1
Digit_Number += 1;
// Check if Current Digit is at Odd or Even Position in Card Number
if (is_even == true)
{
// Multiply Digit by 2
Even_x2_Product = Current_Digit * 2;
// Add Digits of Multiplication Product
while (Even_x2_Product != 0)
{
Even_Digits += Even_x2_Product % 10;
Even_x2_Product /= 10;
}
// Tell Program Next Digit is Odd
is_even = false;
}
else
{
// Add Odd Digits
Odd_Digits += Current_Digit;
// Tell Program Next Number is Even
is_even = true;
}
// Remove Last Digit and Repeat
Card_Number /= 10;
}
// Add Odd and Even Digits Together
Total_Digit_Sum = Even_Digits + Odd_Digits;
// Loop to Check if Card Number is Valid
if (Total_Digit_Sum % 10 == 0)
{
// Check Mastercard
if (Digit_Number == 16)
{
if (Digits_MstrCrd <= 55 && Digits_MstrCrd >= 51)
{
printf("MASTERCARD\n");
}
// Check Visa 16
else if (Digits_Visa_16 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
// Check American Express
else if (Digit_Number == 15)
{
if (Digits_AmEx == 34 || Digits_AmEx == 37)
{
printf("AMEX\n");
}
else
{
printf("INVALID\n");
}
}
// Check Visa 13
else if (Digit_Number == 13)
{
if (Digits_Visa_13 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
else
{
printf("INVALID\n");
}
}
}
What does the program print if Total_Digit_Sum % 10 is not equal to 0? It has no else; there are no commands after the closing } of the block.
I'll show you in your program (with embedded multiline comments) all the objecttionable things I've seen by simple inspection (I've had to modify it a little, as you don't provide an implementation of get_long(char *prompt) function, and some other files you also don't provide. Later I give you a better solution, that doesn't have the problem of the integer limit, as it uses strings to calculate the checksum.
At the end there's a reference to a github repository where all versions of the solution are considered (including a DFA ---Deterministic Finite Automaton--- probably the fastest solution to the problem)
/* sorry, I need to comment this, as you have not provided this
* file. */
//#include <cs50.h>
/* you don't need math.h if you are using only integers */
//#include <math.h>
/* what is needed is stdbool.h, to use booleans in C */
#include <stdbool.h>
#include <stdio.h> /* and stdio, of course */
unsigned long long get_long(char *prmpt)
{
unsigned long long result;
fprintf(stderr, "%s> ", prmpt);
/* this loop is not protected against EOF, so you will have
* to interrupt the program if you reach the end of file
* here. */
while (scanf("%llu", &result) != 1)
fprintf(stderr, "?? >");
return result;
}
int main(void)
{
/* you need a 64bit number, so better use a long long here
* 32bit integers range only up to 4294967296, which is too
* short to use in your problem.
* on dividing your card number by 100000000000000 you'll
* allways get 0.
*/
long Card_Number;
int Digit_Number = 0, Current_Digit = 0, Even_x2_Product = 0, Even_Digits = 0, Odd_Digits = 0,
Total_Digit_Sum = 0;
bool is_even = false;
// Prompt User for Credit Card Number
do
{
Card_Number = get_long("Card Number: ");
}
while (Card_Number < 0);
// Check First Digits of Number
/* don't use pow(3) to produce a constant to divide
* in floating point by a power of ten. It allways
* produces inexact results, ad 1/10 cannot be represented
* as a finite number of digits in base 2. Just use
* 100000000000000LL, instead.
* In order to get the ttype of card, it is better to compare
* the number, as in
* // number is 15digits, at least
* if (Card_number >= 1000000000000000ULL) {
* Digit_number = 15;
* } else if (Card_number >= 10000000000000ULL) {
* Digit number = 14;
* } else if (Card_number >= 1000000000000ULL) {
* Digit_number = 13;
* ...
*/
int Digits_MstrCrd = Card_Number / pow(10, 14);
int Digits_Visa_16 = Card_Number / pow(10, 15);
int Digits_AmEx = Card_Number / pow(10, 13);
int Digits_Visa_13 = Card_Number / (pow(10, 12));
// Loop to determine identity of each digit
while (Card_Number != 0)
{
// Get Last Digit of Number
Current_Digit = (Card_Number % 10);
// Increase Digit Number by 1
/* why do you increment the digit by one, the digit value
* is just that, the remainder of the integer division.
*/
Digit_Number += 1;
// Check if Current Digit is at Odd or Even Position in Card Number
/* better use if (is_even) as is_even is already a
* boolean */
if (is_even == true)
{
// Multiply Digit by 2
Even_x2_Product = Current_Digit * 2;
// Add Digits of Multiplication Product
/* Even_x2_Product cannot be higher that 18,
* so why not just check if it is greater than 10
* and then subtract 10 and add 1 (or better,
* just subtract 9), as in:
if (Even_x2_Product >= 10)
Even_x2_product -= 9;
*/
while (Even_x2_Product != 0)
{
Even_Digits += Even_x2_Product % 10;
Even_x2_Product /= 10;
}
// Tell Program Next Digit is Odd
/* Shouldn't we add this result somewhere,
* mod 10 ??? Like in:
accumulated_checksum += Even_x2_Product;
Note: you do in the odd part.
*/
is_even = false;
}
else
{
/* I suggest you to add all digits together.
* As in:
accumulated_checksum += Current_digit;
*/
// Add Odd Digits
Odd_Digits += Current_Digit;
// Tell Program Next Number is Even
is_even = true;
}
/* if we have added two digits (the accumulated_checksum
* and the calculated one, no possibility of having more
* than 18 as the sum is possible, so check if the result
* is 10 or more, and subtract 10 to eliminate the carry.
if (accumulated_checksum >= 10)
accumulated_checksum -= 10;
*/
// Remove Last Digit and Repeat
Card_Number /= 10;
}
/* you can use only one sum. Both are digits... and if you
* have made the checks suggested above, it is already a number
* modulo 10. */
// Add Odd and Even Digits Together
/* this is not necessary */
Total_Digit_Sum = Even_Digits + Odd_Digits;
// Loop to Check if Card Number is Valid
/* you don't need to calculate the modulo 10 here, as you
* have eliminated all the higher digits in the last loop.
*/
if (Total_Digit_Sum % 10 == 0)
if (Total_Digit_Sum % 10 == 0)
{
// Check Mastercard
/* this is not the number of digits you have, this is the
* integer result of the division by a huge number...
* most of the times this will be zero, but it never be
* 16, with the numbers you are giving for the cards. */
if (Digit_Number == 16)
{
if (Digits_MstrCrd <= 55 && Digits_MstrCrd >= 51)
{
printf("MASTERCARD\n");
}
// Check Visa 16
else if (Digits_Visa_16 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
// Check American Express
/* also this is not true, by the same reason above. */
else if (Digit_Number == 15)
{
if (Digits_AmEx == 34 || Digits_AmEx == 37)
{
printf("AMEX\n");
}
else
{
printf("INVALID\n");
}
}
// Check Visa 13
/* same as above */
else if (Digit_Number == 13)
{
if (Digits_Visa_13 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
else
{
/* so you always end here */
printf("INVALID\n");
}
}
}
There's no need to convert the string of digits into a number... this will make your processing more complicated, and you will need to swith to long long numbers to use it on the longest card numbers.
I have developed this routine:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "proc.h"
int process(const char *str)
{
int l = strlen(str);
const char *p = str + l;
int res = 0;
enum {
ODD_DIGIT,
EVEN_DIGIT,
} pos = ODD_DIGIT;
DEB("processing: [%s]\n", str);
while (--p >= str) {
if (!isdigit(*p)) {
WARN("%s\n", str);
WARN("%*s^: is not a digit\n", (int)(p-str), "");
return -1;
}
int dig = *p - '0';
switch (pos) {
case ODD_DIGIT: pos = EVEN_DIGIT;
DEB("Add dig(%d) to res(%d)\n", dig, res);
res += dig; break;
case EVEN_DIGIT: pos = ODD_DIGIT;
DEB("Add double(dig(%d)) to res(%d)\n", dig, res);
dig <<= 1;
if (dig >= 10)
dig -= 9;
res += dig; break;
}
if (res >= 10)
res -= 10;
DEB("res <= %d\n", res);
}
DEB("Returning => %d\n", res);
if ((flags & FLAG_QUIET) == 0) {
printf("%s: %d\n", str, res);
}
return res;
}
that uses a string of digits, and processes it from right to left (beginning on the end of the string) It is part of this code, published in github and that you can download the complete program from here. You'll find there the version published here, if you checkout the version tagged as SO_60424279, and in the branch master you'll get a table driven DFA implementation that should run faster than this one.
To compile, just execute
make
in the directory you extracted the source.
#include <stdio.h>
#include<cs50.h>
#include <math.h>
int main(void)
{
long x = get_long("enter the credit card number");
int digit = 0, sum = 0;
//digit is used for odd and even checker.
long y = x;
//checksum card digits
while (y != 0)
{
int sumeven = 0, sumodd = 0;
int rem = y % 10;
digit++;
if (digit % 2 == 0) //if digit is even
{
int multiply = rem * 2;
if (multiply == 0)
{
sumeven += multiply;
}
else
{
while (multiply != 0) //adding all digits after
{
sumeven += multiply % 10;
multiply /= 10; //minus last digit of multiply
}
}
}
else //if digit is odd
{
sumodd += rem;
}
y /= 10; //minus last digit from y
sum += sumeven + sumodd;
}
//check for valid credit card
if (digit != 13 && digit != 15 && digit != 16) //for first if
{
printf("INVALID\n");
}
else if (sum % 10 == 0)
{
if (digit == 16) //if digit is 16
{
if (x / 100000000000000 >= 51 && x / 100000000000000 <= 55)
{
printf("MASTERCARD\n");
}
else if (x / 1000000000000000 == 4)
{
printf("VISA\n");
}
else //if digit is not 16
{
printf("INVALID\n");
}
}
else if (digit == 15)
{
if (x / 10000000000000 == 34 || x / 10000000000000 == 37)
{
printf("AMEX\n");
}
else
{
printf("INVALID\n");
}
}
else if (digit == 13)
{
if (x / 1000000000000 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
}
else
{
printf("INVALID\n");
}
}`

Write a program in C to apply Luhn's algorithm for credit card validation

I need my program to prompt a user for an input and re-prompt in case the input doesn't follow a credit card format (ex: negative numbers or letters, etc.) and then apply the algorithm to see if the number is a valid credit card number and if yes, whether it's Visa, MasterCard or AmEx.
I know that this question has been answered with different codes on this website, I swear I read everything that I could possibly find (on this site and elsewhere on the net) but I'm having a really hard time understanding the C syntax and I wanted to try to come up with something myself instead of copying bits of codes I don't understand from other answers. If someone can help me out and look at what I've done so far and tell me what I'm doing wrong I would be really grateful. Also, any tips that could help me make sense of the C syntax logic better would be extremely appreciated.
My program is compiling but when I run it it's acting up in a very weird way: when I enter an input sometimes it will say that it is invalid (even if it's a valid number) and sometimes it will just not return anything after I press enter and won't stop running no matter how many times I press the return key.
Here is my code so far:
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main(void)
{
printf("Please give me your credit card number:\n") ;
long long card_num ;
do
{
card_num = GetLongLong() ;
}
while (card_num < 1 || card_num > 9999999999999999) ;
// Make a copy of the card number to be used and modified throughout the process.
long long temp_num = card_num ;
int digit = 0 ;
int count = 0 ;
int sum_a = 0 ;
int sum_b = 0 ;
// Isolate every digit from the credit card number using a loop and the variable 'digit'.
// Keep track of the amount and position of each digit using variable 'count'.
while (card_num >= 0)
{
digit = card_num % 10 ;
count++ ;
temp_num = (card_num - digit) / 10 ;
break ;
// Apply Luhn's algorithm using two different 'for' loops depending on the position of each digit.
for (count = 0 ; count % 2 == 0 ; count++)
{
sum_a = sum_a + ((card_num % 10) * 2) ;
while ((card_num % 10) * 2 >= 10)
{
sum_a = (sum_a % 10) + 1 ;
}
}
for (count = 0 ; count % 2 != 0 ; count++)
{
sum_b = sum_b + digit ;
}
return sum_a ;
return sum_b ;
return count ;
}
// Checking the validity of the number according to Luhn's algorithm
int total_sum = sum_a + sum_b ;
if (total_sum % 10 != 0)
{
printf("This is an invalid number.\n") ;
}
// If the number entered doesn't have the right amount of digits according
// to variable 'count', declare the number as invalid.
if (count != 13 || count != 15 || count != 16)
{
printf("This is an invalid number.\n") ;
}
// Reset value of variable 'temp_num' and apply calculations that will isolate the first two digits.
// Store the results in a variable 'company_id'.
temp_num = card_num ;
int company_id ;
while (temp_num > 100)
{
temp_num = card_num - (card_num % 10) ;
company_id = temp_num / 10 ;
}
return company_id ;
// Print the type of credit card depending on the company ID and amount of digits.
if (company_id > 50 && company_id < 56 && count == 16)
{
printf("MASTERCARD\n") ;
}
else if ((company_id == 4) && (count == 13 || count == 16))
{
printf("VISA\n") ;
}
else if ((company_id == 34 || company_id == 37) && (count == 15))
{
printf("AMEX\n") ;
}
else
{
printf("This is an invalid number.\n") ;
}
return 0 ;
}
Your answer is a out of order pastiche with sections that don't follow logically from the previous.
Specific issues:
This logic:
if (count != 13 || count != 15 || count != 16)
invalidates every card, the ors (||) should be ands (&&) for this to work.
This loop makes no sense:
while (card_num >= 0)
{
digit = card_num % 10 ;
count++ ;
temp_num = (card_num - digit) / 10 ;
break ;
...
}
The break is unconditional so it exits the loop and ignores the next twenty lines.
You appear to have spliced in subroutines from elsewhere as you call return five times, only the last of which is valid:
return sum_a ;
return sum_b ;
return count ;
return company_id ;
return 0 ;
In several places you use card_num when you should be using temp_num.
You fail to exit the program once you know the card is invalid -- instead you just keep on testing. You fail to acknowledge when a card is valid.
You count the number of digits in the card number but wait until after you run other checks before testing if that digit count was valid or not.
What follows is my rework of your code to address the above and some style issues:
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main(void)
{
printf("Please give me your credit card number: ") ;
long long card_num = 0LL;
while (card_num < 1LL || card_num > 9999999999999999LL)
{
card_num = GetLongLong();
}
// Make a copy of the card number to be used and modified throughout the process.
long long temp_num = card_num;
// Isolate every digit from the credit card number using a loop and the variable 'digit'.
// Keep track of the amount and position of each digit using variable 'count'.
int count = 0;
while (temp_num > 0LL)
{
temp_num = temp_num / 10LL;
count++;
}
// If the number entered doesn't have the right amount of digits according
// to variable 'count', declare the number as invalid.
if (count != 13 && count != 15 && count != 16)
{
printf("This is an invalid number (# of digits).\n");
return 1;
}
// Reset value of variable 'temp_num' and apply calculations that will isolate the first two digits.
// Store the results in a variable 'company_id'.
temp_num = card_num;
while (temp_num > 100LL)
{
temp_num = temp_num / 10LL;
}
int company_id = temp_num;
// Print the type of credit card depending on the company ID and amount of digits.
if (company_id > 50 && company_id < 56 && count == 16)
{
printf("MASTERCARD\n") ;
}
else if ((company_id == 34 || company_id == 37) && (count == 15))
{
printf("AMEX\n") ;
}
else if ((company_id / 10 == 4) && (count == 13 || count == 16 || count == 19))
{
printf("VISA\n") ;
}
else
{
printf("This card was issued by an unknown company.\n");
}
// Apply Luhn's algorithm.
int sum = 0;
temp_num = card_num;
for (int i = 1; i <= count; i++)
{
int digit = temp_num % 10LL;
if (i % 2 == 0)
{
digit *= 2;
if (digit > 9)
{
digit -= 9;
}
}
sum += digit;
temp_num /= 10LL;
}
// Checking the validity of the number according to Luhn's algorithm
if (sum % 10 != 0)
{
printf("This is an invalid number (Luhn's algorithm).\n");
return 1;
}
printf("This is a valid number.\n");
return 0;
}
This is not a finished program -- there's error checking and other details needed. Rather than summing the digits when a doubled card number is greater than 9, I used the simpler approach of subtracting 9.
Here my solution, this method received the var long credit card number, I hope to be helpful.
void check_card(long n)
{
long temp_n = n;
int count = 2;
while(temp_n > 100)
{
temp_n = temp_n / 10;
count ++;
}
long temp_n2 = n;
int sum = 0;
for (int i = 1; i <= count; i++)
{
int digit = temp_n2 % 10;
if (i%2 == 0)
{
if (digit * 2 > 9)
{
sum += (digit * 2) - 9;
}
else
{
sum += digit * 2;
}
}
else
{
sum += digit;
}
temp_n2 /= 10;
}
bool flag = (sum % 10 == 0) ? true : false;
if (count == 15 && (temp_n == 34 || temp_n == 37) && flag)
{
printf("AMEX\n");
}
else if(count == 16 && (temp_n > 50 && temp_n < 56) && flag)
{
printf("MASTERCARD\n");
}
else if((count == 13 || count == 16) && (temp_n / 10 ==4) && flag)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}

Resources