CS50 Luhn's Algorithm in C. Help much appreciated :) - c

I need to solve the problem on the link below, using Luhn's algorithm and what we have learned so far in CS50 (no arrays).
My program compiles but it doesn't identify the card type. What am I doing wrong?
Many thanks in advance!
Problem: https://cs50.harvard.edu/x/2020/psets/1/credit/#:~:text=check50%20cs50/problems/2020/x/credit
/* This program checks if a bank card number is syntactically valid, using Luhn's algorithm */
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main(void)
{
long long n, temp;
int digit, last, first, product;
int lenght = 0;
int sumOdd, sumEven;
// Prompt user for input as long as the inputted number is equal or smaller than zero
do
{
n = get_long_long("Enter your credit card number without special characters:\n");
} while (n < 0);
// Count the number of digits
while (n > 0)
{
n = n/10;
lenght++;
}
// Check if the number's length is valid
if (lenght != 13 && lenght != 15 && lenght != 16)
{
printf("INVALID");
}
// Find the last digit and add it to the even sum
while (n > 0)
{
last = n % 10;
temp = n - last;
sumEven = sumEven + last;
}
/* Starting with second-to-last digit, multiply every other digit by 2. Add those
products together and then add the sum to the sum of the digits that weren't multiplied by 2 */
while (n > 0)
{
digit = n % 10;
temp = n/10;
if (lenght % 2 == 0)
{
product = digit * 2;
sumOdd = sumOdd + product;
} else
{
sumEven = sumEven + digit;
}
// If the last digit of the sum of sums is zero print the number. Else print INVALID.
if (sumOdd + sumEven % 10 != 0)
{
printf("INVALID\n");
}
else
{
printf("%lld\n", n);
}
// Identify the user's card type as per CS50 Credit instructions. Cards commencing with 3 are AMEX, with 5 MasterCard and with 4, VISA.
while (first >= 10)
{
first = n;
first = first / 10;
if (first == 3)
{
printf("AMEX\n");
}
if (first == 5)
{
printf("MASTERCARD\n");
}
if (first == 1)
{
printf ("VISA\n");
}
}
}
}

You have several consecutive while blocks
while (n>0){
// some code
}
while (n>0){
// some code
}
Your program will only exit the first loop when n is no longer larger than 0. When it reaches the next while loop n will still not be larger than 0 so the body of the next while loop will never be entered. Large chunks of your code are not getting executed.

Related

Luhn Algorithm in C

I'm very new to coding and one of the assignment is to program Luhn's Algorithm. After searching on the internet, everyone's solution looks so different and foreign :( so I don't know where the problem is with my solution. Any help is appreciated!
int main(void)
{
// get card number from user
long number;
do
{
number = get_long("Number: ");
} while (number < 0);
// isolate digits of card number
int digit;
int product;
int sum;
int totalSum;
int counter;
for (counter = 1; number > 9; counter++) {
for (int i = 1; number > 9; i = i * -1) {
digit = number % 10;
// isolate digits that need to be multiplied by 2
if (i == 1) {
product = digit * 2;
// add products' digits
if (product > 9) {
sum = (product % 10) + 1;
}
}
// add sum of digits that weren't multiplied by 2
totalSum = product + sum + digit;
// update "new" number
number = (number - digit) / 10;
}
}
// checksum
int check = totalSum % 10;
if (check != 0) {
printf("INVALID\n");
} else {
printf("VALID\n");
}
}
There are a number of errors in your code, most having to do with how you use variables.
You use totalSum without ever initializing it, which means it can start with any random value!
You add both product and sum to totalSum every time, but you only update their values when some condition applies.
This means at least half the time (maybe more) you add old values you already added previously.
Your loops exit when number is 9 or less, meaning you never check the leftmost (highest) digit of number.
As the comments suggested, you should read the pseudo code in Wikipedia, look carefully what they put in each variable, and what they sum and multiply.

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

Validating card credit numbers

I've been trying to create a program that can check if a credit card number is valid or not based on Hans Peter Luhn's algorithm. However, I can only get it to work for some inputs.
// Loop through every digit in the card number
for ( int i = 0; i < intlen (num); ++i )
{
nextDigit = getDigit (num, i);
// If every other number...
if ( i % 2 )
{
nextDigit *= 2;
// ...times by two and add the individual digits to the total
for ( int j = 0; j < intlen (nextDigit); ++j )
{
total += getDigit (nextDigit, j);
}
}
else
{
total += nextDigit;
}
}
When I use the AMEX card number 378282246310005 it works fine and tells the user it's valid. However, once I try the VISA card number 4012888888881881 it says it's invalid. I tried to do a sanity check and do it manually to see if my program was wrong but I deduced the same result. These card number were taken from the Paypal test credit card numbers page so I know they are valid.
So what am I doing wrong?
To clarify the details by the program, if total modulo 10 == 0 then the card number is valid.
Functions called:
// Function to return length (number of digits) of an int
int intlen (long long n)
{
int len = 1;
// While there is more than 1 digit...
while ( abs (n) > 9 )
{
// ...discard leading digits and add 1 to len
n /= 10;
++len;
}
return len;
}
// Function to return a digit in an integer at a specified index
short getDigit (long long num, int index)
{
// Calculating position of digit in integer
int pos = intlen (num) - index;
// Discard numbers after selected digit
while ( pos > 1 )
{
num /= 10;
--pos;
}
// Return right-most digit i.e. selected digit
return num % 10;
}
You'll want to change i % 2 to i % 2 == intlen (num) % 2 or similar; you should double every second digit, but starting from the right; i.e. excluding the final check digit:
From the rightmost digit, which is the check digit, moving left, double the value of every second digit; …
The reason the AMEX number you tried validated anyway is because it's an odd number of digits; the same digits get doubled regardless of whether you skip from the front or the back.
While I was looking at this to find the bug, I re-wrote the program to make it a bit simpler. As a side-effect this will be much faster.
We need to grab digits from the right anyway. We don't even need to count the digits; just keep pulling off the right-most digit until the number becomes 0. If the number starts out as 0, the checksum is trivially 0 and the code is still correct.
I grabbed all the numbers from the test page. This seems to be correct, except for one number: 76009244561 (listed as "Dankort (PBS)" in the test page). I tried this number with the Python code from the Wikipedia page, and again this number is rejected. I don't know why this number is different from the others.
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
bool check_one(long long num)
{
int checksum = 0;
int i = 1;
for (int i = 1; num; num /= 10, ++i)
{
int d = num % 10;
if (i % 2 == 0)
{
// even digit: double and add digits of doubled value
d *= 2;
if (d < 10)
{
// only one digit: we doubled a 0-4 so number is 0-8
checksum += d;
}
else
{
// two digits: we doubled a 5-9 so number is 10-18
checksum += (d % 10);
checksum += (d / 10);
}
}
else
{
// odd digit: just add
checksum += d;
}
}
return (checksum % 10) == 0;
}
static long long const valid_nums[] =
{
378282246310005,
371449635398431,
378734493671000,
5610591081018250,
30569309025904,
38520000023237,
6011111111111117,
6011000990139424,
3530111333300000,
3566002020360505,
5555555555554444,
5105105105105100,
4111111111111111,
4012888888881881,
4222222222222,
76009244561,
5019717010103742,
6331101999990016,
};
static size_t len_valid_nums = sizeof(valid_nums) / sizeof(valid_nums[0]);
static long long const non_valid_nums[] =
{
378282246310006, // add 1 to valid
371449635398432,
378734493671001,
5610591081018205, // swap last two digits
30569309025940,
38520000023273,
601111111111111, // delete last digit
601100099013942,
353011133330000,
};
static size_t len_non_valid_nums =
(sizeof(non_valid_nums) / sizeof(non_valid_nums[0]));
main()
{
bool f;
for (int i = 0; i < len_valid_nums; ++i)
{
long long num = valid_nums[i];
f = check_one(num);
if (!f)
{
printf("Number %lld considered invalid but should be valid\n", num);
}
}
for (int i = 0; i < len_non_valid_nums; ++i)
{
long long num = non_valid_nums[i];
f = check_one(num);
if (f)
{
printf("Number %lld considered valid but should be invalid\n", num);
}
}
}

Extracting individual digits from a long in C

I'm doing a homework assignment for my course in C (first programming course).
Part of the assignment is to write code so that a user inputs a number up to 9 digits long, and the program needs to determine whether this number is "increasing"/"truly increasing"/"decreasing"/"truly decreasing"/"increasing and decreasing"/"truly decreasing and truly increasing"/"not decreasing and not increasing". (7 options in total)
Since this is our first assignment we're not allowed to use anything besides what was taught in class:
do-while, for, while loops, else-if, if,
break,continue
scanf, printf ,modulo, and the basic operators
(We can't use any library besides for stdio.h)
That's it. I can't use arrays or getchar or any of that stuff. The only function I can use to receive input from the user is scanf.
So far I've already written the algorithm with a flowchart and everything, but I need to separate the user's input into it's distinct digits.
For example, if the user inputs "1234..." i want to save 1 in a, 2 in b, and so on, and then make comparisons between all the digits to determine for example whether they are all equal (increasing and decreasing) or whether a > b >c ... (decreasing) and so on.
I know how to separate each digit by using the % and / operator, but I can't figure out how to "save" these values in a variable that I can later use for the comparisons.
This is what I have so far:
printf("Enter a positive number : ");
do {
scanf ("%ld", &number);
if (number < 0) {
printf ("invalid input...enter a positive integer: ");
continue;
}
else break;
} while (1);
while (number < 0) {
a = number % 10;
number = number - a;
number = number / 10;
b = a;
}
Why not scan them as characters (string)? Then you can access them via an array offset, by subtracting the offset of 48 from the ASCII character code. You can verify that the character is a digit using isdigit from ctype.h.
EDIT
Because of the incredibly absent-minded limitations that your professor put in place:
#include <stdio.h>
int main()
{
int number;
printf("Enter a positive number: ");
do
{
scanf ("%ld", &number);
if (number < 0)
{
printf ("invalid input...enter a positive integer: ");
continue;
}
else break;
} while (1);
int a = -1;
int b = -1;
int c = -1;
int d = -1;
int e = -1;
int f = -1;
int g = -1;
int h = -1;
int i = -1;
while (number > 0)
{
if (a < 0) a = number % 10;
else if (b < 0) b = number % 10;
else if (c < 0) c = number % 10;
else if (d < 0) d = number % 10;
else if (e < 0) e = number % 10;
else if (f < 0) f = number % 10;
else if (g < 0) g = number % 10;
else if (h < 0) h = number % 10;
else if (i < 0) i = number % 10;
number /= 10;
}
/* Printing for verification. */
printf("%i", a);
printf("%i", b);
printf("%i", c);
printf("%i", d);
printf("%i", e);
printf("%i", f);
printf("%i", g);
printf("%i", h);
printf("%i", i);
return 0;
}
The valid numbers at the end will be positive, so those are the ones you validate to meet your different conditions.
Since you only need to compare consecutive digits, there is an elegant way to do this without arrays:
int decreasing = 2;
int increasing = 2;
while(number > 9)
{
int a = number % 10;
int b = (number / 10) % 10;
if(a == b)
{
decreasing = min(1, decreasing);
increasing = min(1, increasing);
}
else if(a > b)
decreasing = 0;
else if(a < b)
increasing = 0;
number /= 10;
}
Here, we walk through the number (by dividing by 10) until only one digit remains. We store info about the number up to this point in decreasing and increasing - a 2 means truly increasing/decreasing, a 1 means increasing/decreasing, and a 0 means not increasing/decreasing.
At each step, a is the ones digit and b is the tens. Then, we change increasing and decreasing based on a comparison between a and b.
At the end, it should be easy to turn the values of increasing and decreasing into the final answer you want.
Note: The function min returns the smaller of its 2 arguments. You should be able to write your own, or replace those lines with if statements or conditionals.
It's stupid to ask you to do loops without arrays --- but that's your teacher's fault, not yours.
That being said, I would do something like this:
char c;
while (1) {
scanf("%c", &c);
if (c == '\n') /* encountered newline (end of input) */
break;
if (c < '0' || c > '9')
break; /* do something to handle bad characters? */
c -= '0';
/*
* At this point you've got 0 <= c < 9. This is
* where you do your homework :)
*/
}
The trick here is that when you type numbers into a program, you send the buffer all at once, not one character at a time. That means the first scanf will block until the entire string (i.e. "123823" or whatever) arrives all at once, along with the newline character ( '\n' ). Then this loop parses that string at its leisure.
Edit For testing the increasing/decreasing-ness of the digits, you may think you need to store the entire string, but that's not true. Just define some additional variables to remember the important information, such as:
int largest_digit_ive_seen, smallest_digit_ive_seen, strict_increasing_thus_far;
etc. etc.
Let us suppose you have this number 23654
23654 % 10000 = 2 and 3654
3654 % 1000 = 3 and 654
654 % 100 = 6 and 54
54 % 10 = 5 and 4
4
This way you can get all the digits. Of course, you have to know if the number is greater than 10000, 1000, 100 or 10, in order to know the first divisor.
Play with sizeof to get the size of the integer, in order to avoid a huge if...else statement
EDIT:
Let us see
if (number>0) {
// Well, whe have the first and only digit
} else if (number>10) {
int first_digit = number/10;
int second_digit = number % 10;
} else if (number>100) {
int first_digit = number/100;
int second_digit = (number % 100)/10;
int third_digit = (number % 100) % 10;
} ...
and so on, I suppose
// u_i is the user input, My homework asked me to extract a long long, however, this should also be effective for a long.
int digits = 0;
long long d_base = 1;
int d_arr[20];
while (u_i / d_base > 0)
{
d_arr[digits] = (u_i - u_i / (d_base * 10) * (d_base * 10)) / d_base;
u_i -= d_arr[digits] * d_base;
d_base *= 10;
digits++;
}
EDIT: the extracted individual digit now lives in the int array d_arr. I'm not good at C, so I think the array declaration can be optimized.
Here's a working example in plain C :
#include <stdio.h>
unsigned long alePow (unsigned long int x, unsigned long int y);
int main( int argc, const char* argv[] )
{
int enter_num, temp_num, sum = 0;
int divisor, digit, count = 0;
printf("Please enter number\n");
scanf("%d", &enter_num);
temp_num = enter_num;
// Counting the number of digits in the entered integer
while (temp_num != 0)
{
temp_num = temp_num/10;
count++;
}
temp_num = enter_num;
// Extracting the digits
printf("Individual digits in the entered number are ");
do
{
divisor = (int)(alePow(10.0, --count));
digit = temp_num / divisor;
temp_num = temp_num % divisor;
printf(" %d",digit);
sum = sum + digit;
}
while(count != 0);
printf("\nSum of the digits is = %d\n",sum);
return 0;
}
unsigned long alePow(unsigned long int x, unsigned long int y) {
if (x==0) { return 0; }
if (y==0||x==1) { return 1; }
if (y==1) { return x; }
return alePow(x*x, y/2) * ((y%2==0) ? 1 : x);
}
I would suggest loop-unrolling.
int a=-1, b=-1, c=-1, d=-1, e=1, f=-1, g=-1, h=-1, i=-1; // for holding 9 digits
int count = 0; //for number of digits in the given number
if(number>0) {
i=number%10;
number/=10;
count++;
}
if(number>0) {
h=number%10;
number/=10;
count++;
}
if(number>0) {
g=number%10;
number/=10;
count++;
}
....
....
/* All the way down to the storing variable a */
Now, you know the number of digits (variable count) and they are stored in which of the variables. Now you have all digits and you can check their "decreasing", "increasing" etc with lots of if's !
I can't really think of a better soltion given all your conditions.

How can I get my program to do anything when a "multidigit number with all digits identical" appears?

my program generates random numbers with up to 6 digits with
int number = arc4random % 1000000;
I want that my program do something when a number like 66 or 4444 or 77777 appears (multidigit number with all digits identical). I could manual write:
switch (number) {
case 11: blabla...;
case 22: blabla...;
(...)
case 999999: blabla;
}
That would cost me many program code. (45 cases...)
Is there an easy way to solve the problem.
Here's one way to check that all digits are the same:
bool AllDigitsIdentical(int number)
{
int lastDigit = number % 10;
number /= 10;
while(number > 0)
{
int digit = number % 10;
if(digit != lastDigit)
return false;
number /= 10;
}
return true;
}
As long as you use the mod operator (sorry I do not know objective C) but I'm quite certain there must be a mod operator like % and modding it based on 1's.
For instance:
66%11
You know it is the same number of digits because mod returned 0 in this case.
Same here:
7777%1111
You could figure out how many digits, then divide a six-digit number by 111111, 5-digit number by 11111, etc, and see if the result is an integer.
Excuse me if I don't suggest any Objective-C code, I don't know that language.
convert the number to a string, check the length to get the number of digits, then mod by the appropriate number. pseudocode follows where num_to_check is the number you start out with (i.e. 777)
string my_num = (string)num_to_check;
int num_length = my_num.length;
int mod_result;
string mod_num = "1";
int mod_num_int;
for(int i = 1; i < num_length - 1; i++)
{
mod_num = mod_num + "1";
}
mod_num_int = (int)mod_num;
mod_result = num_to_check % mod_num_int;
//If mod_result == 0, the number was divisible by the appropriate 111... string with no remainder
You could do this recursively with the divide and multiply operator (a divide with remainder could simplify it though)
e.g.
bool IsNumberValid(int number)
{
if(number > 10)
{
int newNumber = number / 10;
int difference = number - newNumber * 10;
number = newNumber;
do
{
newNumber = number / 10;
if((number - newNumber * 10) != difference)
{
// One of the number didn't match the first number, thus its valid
return true;
}
number = newNumber;
} while(number);
// all of the numbers were the same, thus its invalid
return false;
}
// number was <= 10, according to your specifications, this should be valid
return true;
}
Here's a recursive version, just for larks. Again, not the most efficient way, but probably the shortest codewise.
bool IsNumberValid (int number) {
if (number < 10) return true;
int n2 = number / 10;
// Check if the last 2 digits are same, and recurse in to check
// other digits:
return ((n2 % 10) == (number % 10)) && IsNumberValid (n2);
}
Actually, this is tail recursion, so a decent compiler ought to generate pretty efficient code.
Convert to a string and check if each char in the string, starting at position 1, is the same as the previous one.
Assuming Objective-C has a 'bool' type analogous Standard C99:
#include <assert.h>
#include <stdbool.h>
extern bool all_same_digit(int number); // Should be in a header!
bool all_same_digit(int number)
{
static const struct
{
int lo_range;
int divisor;
} control[] =
{
{ 100000, 111111 },
{ 10000, 11111 },
{ 1000, 1111 },
{ 100, 111 },
{ 10, 11 },
};
static const int ncontrols = (sizeof(control)/sizeof(control[0]));
int i;
assert(number < 10 * control[0].lo_range);
for (i = 0; i < ncontrols; i++)
{
if (number > control[i].lo_range)
return(number % control[i].divisor == 0);
}
return(false);
}
You can probably work out a variation where the lo_range and divisor are each divided by ten on each iteration, starting at the values in control[0].
#include <stdlib.h>
#include <stdio.h>
int main() {
int a = 1111;
printf("are_all_equal(%d) = %d\n",a,are_all_equal(a));
a = 143;
printf("are_all_equal(%d) = %d\n",a,are_all_equal(a));
a = 1;
printf("are_all_equal(%d) = %d\n",a,are_all_equal(a));
a = 101;
printf("are_all_equal(%d) = %d\n",a,are_all_equal(a));
return 0;
}
int are_all_equal(int what) {
int temp = what;
int remainder = -1;
int last_digit = -1;
while (temp > 0) {
temp = temp/10;
remainder = temp%10;
if (last_digit != -1 && remainder != 0) {
if (last_digit != remainder) return 0;
}
last_digit = remainder;
}
return 1;
}
Similar, but not exactly equal to the other answers (which I didn't notice were there).
digitsequal = ( ((number < 1000000) && (number > 111110) && (number % 111111 == 0)) ||
...
((number < 1000) && (number > 110) && (number % 111 == 0)) ||
((number < 100) && (number > 10) && (number % 11 == 0))
);
Thanks to boolean operations that shortcut, this should be a good enough solution regarding the average number of comparisons, it requires at most only one modulo operation per number, it has no loop, it can be nicely formatted to look symmetric, and it is obvious what it tests. But of course, premature optimization, you know, but since a lot of other solutions are already given... ;)

Resources