CS50 Credit Card Validation - c

#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;
}

Related

Trying to implement Luhn's Algorithm in C

Iam trying to implement Luhn's algorithm in the C language to check credit card validity, for those who don't know... this is it:
Multiply every other digit by 2, starting with the number’s
second-to-last digit, and then add those products’ digits together.
Add the sum to the sum of the digits that weren’t multiplied by 2.
If the total’s last digit is 0 (or, put more formally, if the total
modulo 10 is congruent to 0), the number is valid!
and to implement that, I looped through the whole number and if the number place I was in had a modulo 2 equal to 0 then I would multiply by two and add to a variable called totalEven.
if that wasn't the case I would add the number I was in to totalOdd without multiplication.
I would then increment the place by one and check the other numbers until I reach 16 (the max digits for a card).
I would later add both variables and check if the total modulo ten was equal to 0. If it means the credit card number is correct, else it is false.
here is the code:
#include <stdio.h>
#include <cs50.h>
//list of variables
//is the card valid
bool isValid = true;
// the creditcard number
long input;
//mod stands for modules, and is used to single out each number as seen later
int mod = 10;
//the location at which number I am checking
int place = 1;
//num is the number I am checking that has been singled out
int num = 0;
//total of numbers * 2 located at locations numbered with even numbers
int totalEven = 0;
//total of numbers located at locations numbered with odd numbers
int totalOdd = 0;
//gets input and stores it in well.. input
input = get_long("Number: ");
// a formula to single out a number, starting with the ones and then as you can see, mod is muliplied by 10 to go over the second number.
num = ((input % mod) - (input % (mod /10))) / (mod/10);
//loops 16 times
for(int i = 0; i < 16; i++)
{
// if the place is even execute below
if(place % 2 == 0)
{
totalEven = totalEven + num * 2;
}
//else do this
else if (place % 2 != 0)
{
totalOdd = totalOdd + num;
}
//moves to the next number
mod = mod * 10;
place++;
}
//fufils the last step of the algorithm
if((totalEven + totalOdd) % 10 == 0 )
{
isValid = true;
}
else
{
isValid = false;
}
problem is that this block of code gives me invalid or !isValid even though the credit card number is supposed to be correct and I checked my "formula" and it works just fine...
I have absolutely no idea what to do... I am a humble hobbyist so plz don't roast me for the monstrosity above.
here is a complete version of the code
#include <stdio.h>
#include <cs50.h>
long power();
int main(void)
{
//AMERX 15 STRT 34 OR 37
//MC 16 STRT 51, 52, 53, 54, 55
//VZA 13 OR 16 STRT 4
long input;
bool isValid = true;
string type;
int mod = 10;
int place = 1;
int num = 0;
int totalEven = 0;
int totalOdd = 0;
do
{
input = get_long("Number: ");
}
while(input < 0);
for(int i = 0; i < 16; i++)
{
num = ((input % mod) - (input % (mod /10))) / (mod/10);
if(place % 2 == 0)
{
totalEven = totalEven + num * 2;
}
else
{
totalOdd = totalOdd + num;
}
mod = mod * 10;
place++;
}
if((totalEven + totalOdd) % 10 == 0 )
{
isValid = true;
}
else
{
isValid = false;
printf("%i , %i", totalEven, totalOdd);
}
if (isValid == true){
if((input < (38 * power(10, 13)) && input >=(37 * power(10, 13))) || (input < (35 * power(10,13)) && input >= (34 * power(10, 13))))
{
type = "AMEX\n";
}
else if(input >= (51 * power(10, 14)) && input < (56 * power(10, 14)))
{
type = "MASTERCARD\n";
}
else if((input < (5 * power(10, 12)) && input >= (4 * power(10, 12))) || (input < (5 * power(10, 15)) && input >= (4 * power(10, 15))))
{
type = "VISA\n";
}
else{
type = "error\n";
}
}
else
{
type = "INVALID\n";
}
if((totalEven + totalOdd) % 10 == 0 )
{
isValid = true;
}
else
{
isValid = false;
}
printf("%s", type);
}
long power(int n, int p)
{
long result = 1;
for(int i = 0; i<p; i++)
{
result = result * n;
}
return result;
I'm not an expert in Luhn algorithm but when I read https://en.wikipedia.org/wiki/Luhn_algorithm it seems to me that you are doing it wrong.
Quote from https://en.wikipedia.org/wiki/Luhn_algorithm :
From the rightmost digit (excluding the check digit) and moving left, double the value of every second digit. The check digit is neither doubled nor included in this calculation; the first digit doubled is the digit located immediately left of the check digit. If the result of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then add the digits of the result (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9) or, alternatively, the same final result can be found by subtracting 9 from that result (e.g., 16: 16 − 9 = 7, 18: 18 − 9 = 9).
I don't see anywhere in your code where you handle that bolded part.
Instead of
totalEven = totalEven + num * 2;
I think you need
int tmp = num * 2;
if (tmp > 9) tmp = tmp - 9;
totalEven = totalEven + tmp;
That said - I think you are making the implementation much more complex than needed by storing the input as a number. Instead of a number you could use an array of digits.
That is - instead of
long input = 1122334455667788
use
int digits[] = {8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1};
// Notice that index zero is the rightmost digit
In this way the algorithm is much more simple:
// Double every second element and check for overflow
for (idx = 1; idx < 16; idx += 2)
{
digits[idx] = 2 * digits[idx];
if (digits[idx] > 9) digits[idx] = digits[idx] - 9;
}
// Calculate the sum
sum = 0;
for (idx = 0; idx < 16; ++idx)
{
sum = sum + digits[idx];
}
If you must receive the input as a number, start by calling a function that converts the number to an array of digits. You can find many, many examples of how that conversion is done here on SO. Here Converting integer into array of digits is just one of many examples.
As I was looking at your code, there some mistakes I want to point out.
You forgot: #include <string.h> as you did declare string type in the code.
input = get_long("Number: "); should have its own do-while loop in case user inputs letters or incorrect numbers.
if(place % 2 == 0){
totalEven = totalEven + num * 2;
}
else if (place % 2 != 0){
totalEven = totalEven + num;
} should totalOdd = totalOdd + num for the second part
totalEven = totalEven + num * 2 is right and wrong at the same time. It only works if the number multiplied by 2 is less than 10. If the num * 2 >= 10, lets say num = 6, then 6 * 2 is 12 which would then be 1 + 2 + totalEven.
num = ((input % mod) - (input % (mod /10))) / (mod/10); This should be in the first for loop.
In #include <math.h>, there is a power function called pow which does exactly as your power() function.
Caution: I have made use of CS50X Library as the question seems to be the one from the same.
#include <stdio.h>
#include <cs50.h>
// Luhn's Algorithm
int main(void)
{
long cardNumber = get_long("Please, enter your card number: ");
int sum1 = 0, num = 0, remainder = 0, sum2 = 0;
long temp = cardNumber;
while (temp > 0)
{
num = ((temp / 10) % 10) * 2; // Multiplying every other digit by 2, starting with the number’s second-to-last digit
while (num > 0)
{
remainder = num % 10;
sum1 += remainder; // Adding those products’ digits together
num /= 10;
}
temp /= 100;
}
// So as to restore the initial values of remainder and temp for the use in next loop
remainder = 0;
temp = cardNumber;
while (temp > 0)
{
remainder = temp % 10;
sum2 += remainder; // Sum of the digits that weren’t multiplied by 2
temp /= 100;
}
((sum1 + sum2) % 10) == 0 ? printf("Valid\n") : printf("Invalid\n");
return 0;
}

CS50 Pset1 Credit: Why isn't my program responding right?

I have basically no coding experience and am enrolled in CS50x on edX. I am currently working on Problem Set 1's Credit problem (in the C language). My program uses Luhn's Algorithm to determine whether a credit card number belongs to American Express, MasterCard, or Visa. When I test the program, inputing test credit card numbers gives me responses I don't want; every valid (test) credit card number I input gives no output.
I've been fighting with this for a few days. There was a point where my program recognized all 3 credit card company numbers, but allowed any random number to be inputted, while I wanted the program to print "INVALID" if that was the case.
Another problem I've had is getting an error for my integer variable, digitcount, (see code below) not being declared as a variable in one line of code (even though there are a few lines of code where digitcount is recognized just fine?)
I had fixed some of these bugs by reading into the error messages I got, but sometimes I can't understand them and just messing around ends up fixing them. It makes no sense to me. (Such as deleting or moving parenthesis and interchanging == with =.)
I have also tried looking at other peoples' codes, but when I try to use similar concepts to what I see, my program doesn't work.
Sorry if any of this is messy or hard to read! Even though I've been trying to teach myself what I need to know to make this work, I often get confused when using variable names like "n" and need some separation between certain parts of the code so I don't feel unorganized.
I could just skip Credit since I've done all the other problems in Problem Set 1, but I really want to learn and use any knowledge I come across to complete and understand as much as I can.
Here's my code! Many thanks to anyone who decides to take a look and help me understand where I'm wrong!
#include <cs50.h>
#include <math.h>
int main(void)
{
long long CCnumber;
//prompts the user for a credit card number
do
{
CCnumber = get_long_long("Enter your Credit Card Number: ");
}
while (CCnumber < 0);
//--------------------
//defines counter as a long type variable and digitcount as an integer type variable
int counter = 0;
int digitcount = 0;
//loop the counts the digit-length of the credit card number
while (counter > 0)
{
counter = CCnumber / 10;
digitcount++;
}
//--------------------
//grabs every other digit in the credit card number, starting from the second to last digit
//% grabs the remainder of a number divided, while / grabs the quotient of the number divided
int digit1 = (((CCnumber % 100) / 10) * 2);
int digit1a = (digit1 / 10);
int digit1b = (digit1 % 10);
int digit2 = (((CCnumber % 10000) / 1000) * 2);
int digit2a = (digit1 / 10);
int digit2b = (digit1 % 10);
int digit3 = (((CCnumber % 1000000) / 100000) * 2);
int digit3a = (digit1 / 10);
int digit3b = (digit1 % 10);
int digit4 = (((CCnumber % 100000000) / 10000000) * 2);
int digit4a = (digit1 / 10);
int digit4b = (digit1 % 10);
int digit5 = (((CCnumber % 10000000000) / 1000000000) * 2);
int digit5a = (digit1 / 10);
int digit5b = (digit1 % 10);
int digit6 = (((CCnumber % 1000000000000) / 100000000000) * 2);
int digit6a = (digit1 / 10);
int digit6b = (digit1 % 10);
int digit7 = (((CCnumber % 100000000000000) / 10000000000000) * 2);
int digit7a = (digit1 / 10);
int digit7b = (digit1 % 10);
int digit8 = (((CCnumber % 10000000000000000) / 1000000000000000) * 2);
int digit8a = (digit1 / 10);
int digit8b = (digit1 % 10);
//adds all the digits together
int checksum1 = (digit1a + digit1b + digit2a + digit2b + digit3a + digit3b + digit4a + digit4b + digit5a + digit5b + digit6a + digit6b +digit7a +digit7b + digit8a +digit8b);
//grabs all the other digits from the credit card number
int otherdigit1 = ((CCnumber % 10) / 1);
int otherdigit2 = ((CCnumber % 1000) / 100);
int otherdigit3 = ((CCnumber % 100000) / 10000);
int otherdigit4 = ((CCnumber % 10000000) / 1000000);
int otherdigit5 = ((CCnumber % 1000000000) / 100000000);
int otherdigit6 = ((CCnumber % 100000000000) / 10000000000);
int otherdigit7 = ((CCnumber % 10000000000000) / 1000000000000);
int otherdigit8 = ((CCnumber % 1000000000000000) / 100000000000000);
//adds all the other digits together
int checksum2 = (otherdigit1 + otherdigit2 + otherdigit3 + otherdigit4 + otherdigit5 + otherdigit6 + otherdigit7 + otherdigit8);
//adds the checksum halfs together
int TOTALchecksum = (checksum1 + checksum2);
//checks the last digit of the total checksum
int lastdigit = (TOTALchecksum / 1);
//--------------------
//determines what the credit card number's first and second digits are (15-digit count)
int startingdigit115 = (CCnumber / 100000000000000);
int startingdigit215 = ((CCnumber / 10000000000000) % 10);
int startingdigitsum15 = (startingdigit115 + startingdigit215);
//determines what the credit card number's first and second digits are (16-digit count)
int startingdigit116 = (CCnumber / 1000000000000000);
int startingdigit216 = ((CCnumber / 100000000000000) % 10);
int startingdigitsum16 = (startingdigit116 + startingdigit216);
//determines what the cred card number's first and second digits are (13-digit count)
int startingdigit113 = (CCnumber / 1000000000000);
int startingdigit213 = ((CCnumber / 100000000000) % 10);
int startingdigitsum13 = (startingdigit113 + startingdigit213);
//--------------------
if ((lastdigit == 0) && (digitcount == 15))
{
//determines if credit card number belongs to American Express
if (startingdigitsum15 == 7 || startingdigitsum15 == 10)
{
printf("AMEX\n");
}
}
else if ((lastdigit == 0) && (digitcount == 16))
{
//determines if credit card number belongs to MasterCard
if (startingdigitsum16 == 6 || startingdigitsum16 == 7 || startingdigitsum16 == 8 || startingdigitsum16 == 9 || startingdigitsum16 == 10)
{
printf("MASTERCARD\n");
}
}
else if ((lastdigit == 0 && digitcount == 13) || (lastdigit == 0 && digitcount == 16))
{
//determines if credit card number belongs to Visa
if ((startingdigit113 == 4 || startingdigit116 == 4))
{
printf("VISA\n");
}
}
else if ((lastdigit == 0 && digitcount != 13) || (lastdigit == 0 && digitcount != 15) || (lastdigit == 0 && digitcount != 16))
{
printf("INVALID\n");
}
}```
Counting leading zeroes is a little hard with numeric input. Maybe you should read the card number as string into a char * array using scanf; then, validate the CCNumber.
...
...
...
// max CCNumber char length is 16, plus 1 for string terminator '\0' = 17 chars
char CCNumber[17];
int notValidInput = 1;
int digitCount = 0;
while (notValidInput) {
notValidInput = 0;
scanf("%s", CCNumber);
digitCount = 0;
while (CCNumber[digitCount] != '\0') {
// CCNumber character validation
if (CCNumber[digitCount] < '0' || CCNumber[digitCount] > '9') {
notValidInput = 1;
printf("error: must contain numeric chars.\n");
break;
}
else {
digitCount++;
}
}
...
...
...
}

Detect valid credit card number algorithm

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");
}
}

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");
}
}`

Why do i keep getting an invalid feedback?

I've been working on the cs50 pset1 credit for the past 36 hours.
My code is not generating any errors but when i enter my card details it shows invalid, what could be the problem.
I'm a noob, guys please help out.
#include <stdio.h>
#include <cs50.h>
int main (void)
{
long long ccnumber;
do
{
//Prompting the user for his creditcard number
ccnumber=get_long_long("What is your creditcard number\n");
}
while (ccnumber<= 0);
//validating the credit card number to ascertain if its upto 13. 14 or 16
long long numbers = ccnumber;
int totaldigits = 0;
while (numbers > 0)
{
numbers = numbers/10;
totaldigits++;
}
if(totaldigits != 13 || totaldigits !=14 || totaldigits !=16)
{
printf("Invalid\n");
}
//Starting from the second to last digit
//And multiplying through by 2
int number1 = ((ccnumber/10) % 10) *2;
int number2 = ((ccnumber/1000) % 10) *2;
int number3 = ((ccnumber/100000) % 10) *2;
int number4 = ((ccnumber/10000000) % 10) *2;
int number5 = ((ccnumber/1000000000) % 10) *2;
int number6 = ((ccnumber/100000000000) % 10) *2;
int number7 = ((ccnumber/10000000000000) % 10) *2;
int number8 = ((ccnumber/10000000000000000) % 10) *2;
//Getting the Sum of the first number series
int sum1;
sum1 = (number1 + number2 + number3 + number4 + number5 + number6 + number7 + number8);
//Populating the second number series
int number9 = ccnumber % 10;
int number10 = ((ccnumber/100) % 10);
int number11 = ((ccnumber/10000) % 10);
int number12 = ((ccnumber/1000000) % 10);
int number13 = ((ccnumber/100000000) % 10);
int number14 = ((ccnumber/10000000000) % 10);
int number15 = ((ccnumber/1000000000000) % 10);
int number16 = ((ccnumber/100000000000000) % 10);
int sum2;
sum2 =(number9 + number10 + number11 + number12 + number13 + number14 + number15 + number16);
//Adding sum1 and sum2
int checksum;
checksum = sum1 + sum2;
if (checksum % 10 != 0)
{
printf("invalid/n");
}
//Verifying for visacard(1)
if(totaldigits == 13)
{
printf("Visa\n");
}
// Verifying for visa(2)
if(totaldigits == 16)
{
long long visa;
visa = ccnumber/1000000000000000;
if(visa!=4)
{
printf("Invalid\n");
}
else
{
printf("VISA\n");
}
//Verifying for MasterCard
long long master;
master = ccnumber/100000000000000;
if(master!=51 || master!=52 ||master!=53 ||master!=54 ||master!=55)
{
printf("Invalid\n");
}
else
{
printf("MASTER\n");
}
}
//Verifying for Amex
}
/counter
What is your creditcard number
2221000000000009
Invalid
invalid/nInvalid
Invalid
First of all, some of your if conditions are wrong. For example,
if(totaldigits != 13 || totaldigits != 14 || totaldigits != 16)
You want to check if the totaldigits is 13 or 14 or 16, but what you check here is if totaldigits is not 13, then print invalid or if totaldigits is not 14 then, print invalid and so on. Hence, it will always print invalid. You need to use && (AND) instead of || (OR).
The same problem exists in this line as well.
if(master!=51 || master!=52 ||master!=53 ||master!=54 ||master!=55)
Another problem is that you put 1 unnecessary 0 to division in this line, so it always yields 0.
int number8 = ((ccnumber/10000000000000000) % 10) *2; // 1 zero unnecessary
As it is suggested in the comments, you may need to consider creating an array for storing individual digits instead of using 16 different integers. When you have an array you can do the divisions in a for loop so that you don't get confused with a lot of zeros.
As an example, you can refer to this link
A solution for cs50's credit card validation problem .

Resources