-1

#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.

  1. Determine if card is 15 || 16 || 13 digits if not mark In valid in IF Else loop
  2. Use CC check sum formula If else loop (In valid if it doesn't meet Criteria)
  3. 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 );
    
    
    }
    
Mike
  • 13
  • 4
  • 2
    "The Length count loop is fine until 10 digits but then pulls random numbers", you're storing a `long` variable inside an `int`, [thereby *potentially* losing the actual value](https://stackoverflow.com/a/13652624/10305477). This is called [Undefined Behavior](https://en.cppreference.com/w/c/language/behavior) and is the reason behind "pulling random numbers". That length loop is fine, the `x`, however, is not – Chase Jul 22 '20 at 04:40
  • 2
    Don't treat the CCN as an integer; treat it as a string (of digits). It's a lot easier to process the string — and to determine how many digits are present, and so on. – Jonathan Leffler Jul 22 '20 at 04:52

1 Answers1

5

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;
}
Chase
  • 5,315
  • 2
  • 15
  • 41
  • Good effort and helpfulness, though the specific question was a bit vague. – David C. Rankin Jul 22 '20 at 06:03
  • Good answer, but the processing should be done on a string, not on a long. That would be much simpler. – Jabberwocky Jul 22 '20 at 06:44
  • @Jabberwocky It would! It'd even tone down on the number of loops it could need. I agree, but I think cs50 specifically wants you to work with longs for this one. Not quite sure on that though. All I know is they want you to use `get_long` to get the input, would have to turn that into a string manually. As opposed to just getting string input in the first place. – Chase Jul 22 '20 at 06:54
  • @Chase Chase thank you so much!!! The long with the int was the issue for me. I edited my post so anyone can see what I ended up submitting. I need to study what you sent. Yours is a lot simpler and easy to understand. I am not great with functions yet. I did weeks 0-5 then got called back to work took 4 weeks off and then got laid off again due to COVID. I am going back weeks 1-5 and doing the harder optional problem. – Mike Jul 22 '20 at 19:15
  • @chase I can not thank you enough. Your answer will help me a lot. I am going to compare it to mine and work on getting to that level. I can't say thank you enough it is going to be a major help for me grabbing the next level on this. – Mike Jul 22 '20 at 19:16