Adding modulo 128-bit numbers - arrays

I have some 2 numbers of 128 bits. Let it be the same number:
A=282434364544378924672110924168367615433
B=282434364544378924672110924168367615433
It is necessary to add them modulo numbers
340282366920938463460374607431768211337
To represent 128-bit numbers, I use two 64-bit arrays
low_A = A.aa[0];
low_B = B.aa[0];
low_M = M.aa[0];
high_A = A.aa[1];
high_B = B.aa[1];
high_M = M.aa[1];
Thus, selecting the lower and upper parts (we can roughly say that in this way the numbers will be presented to the 64th number system).
The problem is that when adding the numbers A and B, an overflow occurs physically. The transfer is performed to a non-existent bit, although the binary representation remains true. If there was a transfer, we certainly already know that the given number is greater than the modulus.
How then do we explain to the machine what the result should be
A+B-M
if (high_A <= ULLONG_MAX - high_B) flag_h = 0; else flag_h = 1;
if (flag_h) {
int car = 0;
high_A = high_A - high_M;
high_B = high_B - high_M;
high_C = high_A + high_B + high_M;
if (low_C <= low_M)
{
low_C = low_M - low_C;
low_C = ULLONG_MAX - low_C + 1;
car = 1;
}
else { low_C = low_C - low_M; }
high_C -= car;
}
I tried to do this in the above manner, but still the program finds it wrong.
I explain what I wanted to do. I tried to make a mathematical formula (A-M) + (B-M) + M = (A + B-M). I'm trying to subtract from the senior and junior level.
Let's show on the numbers
_ 51
38
1) 8-1 = 7, 7 more will have to be subtracted
2) We simulate a loan at the senior level
3) 10-7 = 9 + 1 - 7 = 3
4) Set loan flag in the unit
5) 5 - 3 - flag = 1
6) 13

I found the solution to the problem, began to subtract from the higher order, here is the code
if (flag_h)
{
int car = 0;
unsigned __int64 temp1,temp2;
if (high_C < high_M)
{
temp1 = high_M - high_C;
high_C = ULLONG_MAX - temp1 + 1;
}
else { high_C = high_C - high_M; }
if (low_C < low_M) {
temp2 = low_M - low_C;
low_C = ULLONG_MAX - temp2 + 1;
high_C--;
}
else { low_C = low_C - low_M; }
}

Related

CS50 - Validating Credit Card Numbers in C Language [duplicate]

This is the credit problem from CS50x
I wrote this code on Windows and it didn't work there. After entering a 15 digit card number on windows it wasn't accepting the number and was prompting me again and again.
But when i pasted the same code on CS50's cloudspace in VSCode which is on linux, the code runs perfectly fine there.
The issue seems to be in the get_long function, it wasnt letting me input a 15 digit card number on windows.
#include <stdio.h>
#include "cs50.h"
int main()
{
long number;
number = get_long("Enter your card number: ");
//Check length of number
int i = 0;
long length = number;
while (length > 0 )
{
length = length / 10;
i++;
}
if (i != 16 && i != 15 && i!= 13)
{
printf ("INVALID");
}
else
{
int sum1 = 0;
int sum2 = 0;
long x = number;
int mod1 = 0;
int mod2 = 0;
int d1 = 0;
int d2 = 0;
int final_sum = 0;
do
{
//Remove last digit and add to sum1
mod1 = x % 10;
x = x / 10;
sum1 = sum1 + mod1;
//Remove second last digit
mod2 = x % 10;
x = x / 10;
//Multiply second last digit by two
mod2 = mod2 * 2;
//Extract the digits
d1 = mod2 % 10;
d2 = mod2 / 10;
//Add to sum2
sum2 = sum2 + d1 + d2;
} while (x > 0 );
final_sum = sum1 + sum2;
//Check Luhns Algorithm
if (final_sum % 10 != 0)
{
printf("INVALID\n");
}
else
{
long y = number;
int j = 0;
do
{
y = y / 10;
j++;
} while (y > 100 );
if ( y / 10 == 4)
{
printf ("VISA");
}
else if ((y / 10 == 5) && (0 < y % 10 && y % 10 < 6))
{
printf("MASTERCARD");
}
else if ((y / 10 == 3) && (y % 10 ==4 || y % 10 == 7) )
{
printf("AMEX");
}
}
}
}
Your code works because on your Linux platform, type long happens to be an 8-byte, 64-bit type that can easily handle integers matching even 15- or 16-digit credit card numbers.
Your code fails on Windows because there, type long is a 32-bit type, not capable of handling numbers greater than 2,147,483,647 or 4,294,967,295 (depending on whether signed or unsigned).
This discrepancy between the two platforms is not a bug: C's short, int, and long types have always been somewhat loosely defined. The requirement for type long is, in effect, that it have at least 32 bits, which on both platforms it certainly does.
It was rather unfair and misleading for your assignment to have said "Best to use get_long from CS50’s library to get users’ input". This is a nonportable programming practice for this task.
In portable C, type long long is guaranteed to be at least 64 bits long, so it would work reliably here. I do not know if CS50's library has a get_long_long function. (Update: per comments below, it does.)
In the long run, it's generally easier to deal with credit card numbers as strings, not integers. But during week 1 of CS50, students haven't learned about strings yet.
The discussion in the comments of 32-bit v. 64-bit should be educational for you, and I hope that you can integrate that information into your program.
Compliments to the layout and logic of your beginner code. Very clear and very easy to read and follow (including appropriate comments.) Keep this up and you will become a fine programmer!
I've revised your code (without appreciably changing any of its logic) to suggest how it may be slightly improved. The following is offered for your consideration. It is your code only slightly modified. It's up to you to consider if you want to apply any of these "alterations" to your code.
#include <stdio.h>
#include "cs50.h"
int main()
{
long number = get_long("Enter A card number: ");
long x = number; // will reload and reuse x several times
int i = 0; // handy to have around
// Check length of number
while (x > 0 )
{
x = x / 10;
i++;
}
if (i != 16 && i != 15 && i!= 13)
{
printf ("INVALID");
return 1; // early termination saves one level of indentation
}
int sum1 = 0;
int sum2 = 0;
int mod1 = 0;
int mod2 = 0;
int d1 = 0;
int d2 = 0;
int final_sum = 0;
x = number; // reload
while ( x > 0 )
{
//Remove last digit and add to sum1
mod1 = x % 10;
x = x / 10;
sum1 = sum1 + mod1;
//Remove second last digit
mod2 = x % 10;
x = x / 10;
//Multiply second last digit by two
mod2 = mod2 * 2;
//Extract the digits
d1 = mod2 % 10;
d2 = mod2 / 10;
//Add to sum2
sum2 = sum2 + d1 + d2;
}
final_sum = sum1 + sum2;
//Check Luhns Algorithm
if (final_sum % 10 != 0)
{
printf("INVALID\n");
return 1; // early termination
}
x = number; // reload
while ( x > 99 ) // NB! subtle change!!
{
x = x / 10; // shrink until 10 <= x <= 99
}
if ( 40 <= x && x <= 49 ) // check this
{
printf ("VISA");
}
else if ( 50 <= x && x < 56 ) // check this
{
printf("MASTERCARD");
}
else if ( x == 34 || x == 37 ) // check this
{
printf("AMEX");
}
else
{
printf("INVALID\n");
return 1; // early termination
}
return 0;
}
The instructions for your task state the following:
But do not assume that the user’s input will fit in an int! Best to use get_long from CS50’s library to get users’ input.
This advice is misleading when using the Microsoft Windows platform.
On Microsoft Windows, the data type long has a width of only 32 bits, whereas on most other common platforms, such Linux and MacOS, it has a width of 64 bits. The ISO C standard only requires long to have a minimum width of 32 bits, so all the mentioned operating systems are complying with the standard in this respect.
A 32-bit signed integer is able to represent numbers up to 2,147,483,647, whereas a 64-bit signed integer is able to represent numbers up to 9,223,372,036,854,775,807. Therefore, a 32-bit integer data type is insufficient to represent a credit card number; you require a 64-bit integer data type.
In constrast to long, the data type long long is guaranteed to be at least 64 bits wide on all platforms.
For the reasons stated above, if you want your program to work on Microsoft Windows, you will have to use the long long data type to represent a credit card number, instead of long. You will also have to use the function get_long_long instead of get_long.

Same code works on Linux but not on windows

This is the credit problem from CS50x
I wrote this code on Windows and it didn't work there. After entering a 15 digit card number on windows it wasn't accepting the number and was prompting me again and again.
But when i pasted the same code on CS50's cloudspace in VSCode which is on linux, the code runs perfectly fine there.
The issue seems to be in the get_long function, it wasnt letting me input a 15 digit card number on windows.
#include <stdio.h>
#include "cs50.h"
int main()
{
long number;
number = get_long("Enter your card number: ");
//Check length of number
int i = 0;
long length = number;
while (length > 0 )
{
length = length / 10;
i++;
}
if (i != 16 && i != 15 && i!= 13)
{
printf ("INVALID");
}
else
{
int sum1 = 0;
int sum2 = 0;
long x = number;
int mod1 = 0;
int mod2 = 0;
int d1 = 0;
int d2 = 0;
int final_sum = 0;
do
{
//Remove last digit and add to sum1
mod1 = x % 10;
x = x / 10;
sum1 = sum1 + mod1;
//Remove second last digit
mod2 = x % 10;
x = x / 10;
//Multiply second last digit by two
mod2 = mod2 * 2;
//Extract the digits
d1 = mod2 % 10;
d2 = mod2 / 10;
//Add to sum2
sum2 = sum2 + d1 + d2;
} while (x > 0 );
final_sum = sum1 + sum2;
//Check Luhns Algorithm
if (final_sum % 10 != 0)
{
printf("INVALID\n");
}
else
{
long y = number;
int j = 0;
do
{
y = y / 10;
j++;
} while (y > 100 );
if ( y / 10 == 4)
{
printf ("VISA");
}
else if ((y / 10 == 5) && (0 < y % 10 && y % 10 < 6))
{
printf("MASTERCARD");
}
else if ((y / 10 == 3) && (y % 10 ==4 || y % 10 == 7) )
{
printf("AMEX");
}
}
}
}
Your code works because on your Linux platform, type long happens to be an 8-byte, 64-bit type that can easily handle integers matching even 15- or 16-digit credit card numbers.
Your code fails on Windows because there, type long is a 32-bit type, not capable of handling numbers greater than 2,147,483,647 or 4,294,967,295 (depending on whether signed or unsigned).
This discrepancy between the two platforms is not a bug: C's short, int, and long types have always been somewhat loosely defined. The requirement for type long is, in effect, that it have at least 32 bits, which on both platforms it certainly does.
It was rather unfair and misleading for your assignment to have said "Best to use get_long from CS50’s library to get users’ input". This is a nonportable programming practice for this task.
In portable C, type long long is guaranteed to be at least 64 bits long, so it would work reliably here. I do not know if CS50's library has a get_long_long function. (Update: per comments below, it does.)
In the long run, it's generally easier to deal with credit card numbers as strings, not integers. But during week 1 of CS50, students haven't learned about strings yet.
The discussion in the comments of 32-bit v. 64-bit should be educational for you, and I hope that you can integrate that information into your program.
Compliments to the layout and logic of your beginner code. Very clear and very easy to read and follow (including appropriate comments.) Keep this up and you will become a fine programmer!
I've revised your code (without appreciably changing any of its logic) to suggest how it may be slightly improved. The following is offered for your consideration. It is your code only slightly modified. It's up to you to consider if you want to apply any of these "alterations" to your code.
#include <stdio.h>
#include "cs50.h"
int main()
{
long number = get_long("Enter A card number: ");
long x = number; // will reload and reuse x several times
int i = 0; // handy to have around
// Check length of number
while (x > 0 )
{
x = x / 10;
i++;
}
if (i != 16 && i != 15 && i!= 13)
{
printf ("INVALID");
return 1; // early termination saves one level of indentation
}
int sum1 = 0;
int sum2 = 0;
int mod1 = 0;
int mod2 = 0;
int d1 = 0;
int d2 = 0;
int final_sum = 0;
x = number; // reload
while ( x > 0 )
{
//Remove last digit and add to sum1
mod1 = x % 10;
x = x / 10;
sum1 = sum1 + mod1;
//Remove second last digit
mod2 = x % 10;
x = x / 10;
//Multiply second last digit by two
mod2 = mod2 * 2;
//Extract the digits
d1 = mod2 % 10;
d2 = mod2 / 10;
//Add to sum2
sum2 = sum2 + d1 + d2;
}
final_sum = sum1 + sum2;
//Check Luhns Algorithm
if (final_sum % 10 != 0)
{
printf("INVALID\n");
return 1; // early termination
}
x = number; // reload
while ( x > 99 ) // NB! subtle change!!
{
x = x / 10; // shrink until 10 <= x <= 99
}
if ( 40 <= x && x <= 49 ) // check this
{
printf ("VISA");
}
else if ( 50 <= x && x < 56 ) // check this
{
printf("MASTERCARD");
}
else if ( x == 34 || x == 37 ) // check this
{
printf("AMEX");
}
else
{
printf("INVALID\n");
return 1; // early termination
}
return 0;
}
The instructions for your task state the following:
But do not assume that the user’s input will fit in an int! Best to use get_long from CS50’s library to get users’ input.
This advice is misleading when using the Microsoft Windows platform.
On Microsoft Windows, the data type long has a width of only 32 bits, whereas on most other common platforms, such Linux and MacOS, it has a width of 64 bits. The ISO C standard only requires long to have a minimum width of 32 bits, so all the mentioned operating systems are complying with the standard in this respect.
A 32-bit signed integer is able to represent numbers up to 2,147,483,647, whereas a 64-bit signed integer is able to represent numbers up to 9,223,372,036,854,775,807. Therefore, a 32-bit integer data type is insufficient to represent a credit card number; you require a 64-bit integer data type.
In constrast to long, the data type long long is guaranteed to be at least 64 bits wide on all platforms.
For the reasons stated above, if you want your program to work on Microsoft Windows, you will have to use the long long data type to represent a credit card number, instead of long. You will also have to use the function get_long_long instead of get_long.

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

How to add product digits rather than products themselves in C?

I am trying to finish an assignment in C for the CS50 course in which I must implement Luhn's algorithm to validate a credit card number. Here is a quick example to elaborate:
credit card number: 4003600000000014.
Now for every other digit, starting with the number’s second-to-last digit:
1-0-0-0-0-6-0-4
Let’s multiply each of the digits by 2:
1•2 + 0•2 + 0•2 + 0•2 + 0•2 + 6•2 + 0•2 + 4•2
That gives us:
2 + 0 + 0 + 0 + 0 + 12 + 0 + 8
Now let’s add those products’ digits (i.e., not the products themselves) together:
2 + 0 + 0 + 0 + 0 + 1 + 2 + 0 + 8 = 13
Now let’s add that sum (13) to the sum of the digits that weren’t multiplied by 2 (starting from the end):
13 + 4 + 0 + 0 + 0 + 0 + 0 + 3 + 0 = 20
Yup, the last digit in that sum (20) is a 0, so the number is valid.
I figured out how to extract each number in the credit card individually (I know my way is boring and probably not practical), so the next step is to multiply every other number by two and add (the products' digits, not the digits themselves) and this is what I need help of how to do it?
MY code:
#include <cs50.h>
#include <stdio.h>
#include <math.h>
int main(void)
{
long credit_card_number;
do
{
credit_card_number = get_long("Enter your credit card number: ");
}
while (credit_card_number < 1 || credit_card_number > 9999999999999999);
//American Express uses 15-digit numbers. American Express numbers start with 34 or 37
//MasterCard uses 16-digit numbers. MasterCard numbers start with 51, 52, 53, 54, or 55.
//Visa uses 13- and 16-digit numbers. Visa numbers start with 4.
// checksum
long last_number;
long credit_card_without_last_number;
long second_to_last_number;
long credit_card_without_second_number;
long third_number;
long credit_card_without_third_number;
long fourth_number;
long credit_card_without_fourth_number;
long fifth_number;
long credit_card_without_fifth_number;
long sixth_number;
long credit_card_without_sixth_number;
long seventh_number;
long credit_card_without_seventh_number;
long eighth_number;
long credit_card_without_eighth_number;
long ninth_number;
long credit_card_without_ninth_number;
long tenth_number;
long credit_card_without_tenth_number;
long eleventh_number;
long credit_card_without_eleventh_number;
long twelfth_number;
long credit_card_without_twelfth_number;
long thirteenth_number;
long credit_card_without_thirteenth_number;
long fourteenth_number;
long credit_card_without_fourteenth_number;
long fifteenth_number;
long credit_card_without_fifteenth_number;
long sixteenth_number;
long multiply_digits;
//separating each number starting from the last (right)in its own variable.
last_number = credit_card_number % 10;
credit_card_without_last_number = credit_card_number / 10;
second_to_last_number = credit_card_without_last_number % 10;
credit_card_without_second_number = credit_card_without_last_number / 10;
third_number = credit_card_without_second_number % 10;
credit_card_without_third_number = credit_card_without_second_number / 10;
fourth_number = credit_card_without_third_number % 10;
credit_card_without_fourth_number = credit_card_without_third_number / 10;
fifth_number = credit_card_without_fourth_number % 10;
credit_card_without_fifth_number = credit_card_without_fourth_number / 10;
sixth_number = credit_card_without_fifth_number % 10;
credit_card_without_sixth_number = credit_card_without_fifth_number / 10;
seventh_number = credit_card_without_sixth_number % 10;
credit_card_without_seventh_number = credit_card_without_sixth_number / 10;
eighth_number = credit_card_without_seventh_number % 10;
credit_card_without_eighth_number = credit_card_without_seventh_number / 10;
ninth_number = credit_card_without_eighth_number % 10;
credit_card_without_ninth_number = credit_card_without_eighth_number / 10;
tenth_number = credit_card_without_ninth_number % 10;
credit_card_without_tenth_number = credit_card_without_ninth_number / 10;
eleventh_number = credit_card_without_tenth_number % 10;
credit_card_without_eleventh_number = credit_card_without_tenth_number / 10;
twelfth_number = credit_card_without_eleventh_number % 10;
credit_card_without_twelfth_number = credit_card_without_eleventh_number / 10;
thirteenth_number = credit_card_without_twelfth_number % 10;
credit_card_without_thirteenth_number = credit_card_without_twelfth_number / 10;
fourteenth_number = credit_card_without_thirteenth_number % 10;
credit_card_without_fourteenth_number = credit_card_without_thirteenth_number / 10;
fifteenth_number = credit_card_without_fourteenth_number % 10;
credit_card_without_fifteenth_number = credit_card_without_fourteenth_number / 10;
sixteenth_number = credit_card_without_fifteenth_number % 10;
//Here I need the help to multiply these numbers by two and then add each product's
//digits to the rest of the unused numbers.
multiply_digits = (second_to_last_number*2)+(fourth_number*2)+(sixth_number*2)+(eighth_number*2)+(tenth_number*2)+(twelfth_number*2)+(fourteenth_number*2)+(sixteenth_number*2);
}
Try doing this instead
int main(){
long cNo = 4003600000000014;
int arr[16];
for(int i=0; i<16; i++){
arr[15-i] = cNo % 10;
cNo /= 10;
}
int multipliedSum = 0;
for(int i=0; i<16; i++){
if(i%2==1)
multipliedSum += arr[i];
else{
if(arr[i]*2<10){
multipliedSum += (arr[i]*2);
}else{
int num = arr[i]*2;
while(num){
multipliedSum += num%10;
num/=10;
}
}
}
}
printf("valid = %s\n",multipliedSum%10==0?" True": " False");
}
You will get the following
valid = True
A general algorithm for adding digits (assuming an integer type):
Initialize your sum to 0: sum = 0
Extract the lowest digit from the number using the % modulus operator: digit = number % 10
Add the value of that digit to the sum: sum += digit (shorthand for sum = sum + digit)
Divide the number by 10: number /= 10 (shorthand for number = number / 10
If the number is non-zero after dividing by 10, go back to 2
End
The modulus operator % returns the integer remainder of an integer division - 123 / 10 == 12 rem 3. So the remainder of dividing the number by 10 is the least significant decimal digit of the number. Notice that integer division gives you an integer result - 123 / 10 == 12, not 12.3.
You'll want to put this in a separate function, so you can write something like
int sumdig( int v )
{
...
}
int main( void )
{
int value = 123;
int sum = sumdig( value ); // sumdig will return 1 + 2 + 3, or 6
...
}
When you find yourself creating a bunch of separate variables of the same type with the same name except for some tacked-on ordinal (var1, var2, var3 or first_thing, second_thing, third_thing), that's a real strong hint you want to use an array. You can use an array to store the individual digits of your card number:
int number[16];
and use the % 10 method as described above to extract the individual digits:
long tmp = credit_card_number; // use a temporary so we preserve the original card number
for ( int i = 0; i < 16; i++ )
{
number[i] = tmp % 10;
tmp /= 10;
}
This means that the least significant (rightmost) card number digit will be stored in number[0] and the most significant (leftmost) card number digit will be stored in number[15], so be aware of that. For the purposes of validating the number it doesn't matter, but if you want to display the contents of the array you'll have to take that into account.
Using an array makes it easier to extract subsets of digits:
for ( int i = 1; i < 16; i += 2 ) // hit every other element starting at element 1
{
number[i] *= 2; // multiply these digits by 2
}
That loop above executes the "1•2 + 0•2 + 0•2 + 0•2 + 0•2 + 6•2 + 0•2 + 4•2" portion of your algorithm.
You should be able to figure out the rest from there. Hope this helps.
Hint: to extract one digit from a number, mod it by 10.
So say that you want to figure out the sum of the digits of a number, say 123456, you will do the following:
(pseudocode)
number=123456;
sum=0;
loop if number is not 0{
sum+=number % 10;
number-=number % 10;
number=(int)(number/10);
}
Now try to implement it as a function, say digit(), and when you are trying to add some numbers digit-wise, say 123 and 456, just do digit(123)+digit(456) instead.

Finding a brute force algorithm for the following cryptarithm / alphametic puzzle

I'm trying to write a program in C that will solve the following cryptarithm:
one + one = two
seven is prime
nine is a perfect square
Namely, I need to find the numerical values for the words one, two, seven and nine where each letter (o, n, e, t, w, s, v, i) is assigned a numerical value and the complete number also meets all of the above conditions.
I was thinking along the lines of creating an int array for each of the words and then 1) checking if each word meets the condition (e.g is a prime for "seven") and then 2) checking if each integer in the array is consistant with the value of the other words, where the other words also are found to meet their respective conditions.
I can't really see this working though as I would have to continuously convert the int array to a single int throughout every iteration and then I'm not sure how I can simultaneously match each element in the array with the other words.
Perhaps knowing the MIN and MAX numerical range that must be true for each of the words would be useful?
Any ideas?
For a brute-force (ish) method, I'd start with the prime seven, and use the Sieve of Eratosthenes to get all the prime numbers up to 99999. You could discard all answers where the 2nd and 4th digit aren't the same. After that you could move on to the square nine, because three of the digits are determined by the prime seven. That should narrow down the possibilities nicely, and then you can just use the answer of #pmg to finish it off :-).
Update: The following C# program seems to do it
bool[] poss_for_seven = new bool[100000]; // this will hold the possibilities for `seven`
for (int seven = 0; seven < poss_for_seven.Length; seven++)
poss_for_seven[seven] = (seven > 9999); // `seven` must have 5 digits
// Sieve of Eratosthenes to make `seven` prime
for (int seven = 2; seven < poss_for_seven.Length; seven++) {
for (int j = 2 * seven; j < poss_for_seven.Length; j += seven) {
poss_for_seven[j] = false;
}
}
// look through the array poss_for_seven[], considering each possibility in turn
for (int seven = 10000; seven < poss_for_seven.Length; seven++) {
if (poss_for_seven[seven]) {
int second_digit = ((seven / 10) % 10);
int fourth_digit = ((seven / 1000) % 10);
if (second_digit == fourth_digit) {
int e = second_digit;
int n = (seven % 10); // NB: `n` can't be zero because otherwise `seven` wouldn't be prime
for (int i = 0; i < 10; i++) {
int nine = n * 1000 + i * 100 + n * 10 + e;
int poss_sqrt = (int)Math.Floor(Math.Sqrt(nine) + 0.1); // 0.1 in case of of rounding error
if (poss_sqrt * poss_sqrt == nine) {
int o = ((2 * e) % 10); // since 2 * `one` = `two`, we now know `o`
int one = o * 100 + n * 10 + e;
int two = 2 * one;
int t = ((two / 100) % 10);
int w = ((two / 10) % 10);
// turns out that `one`=236, `two`=472, `nine` = 3136.
// look for solutions where `s` != `v` with `s` and `v' different from `o`, `n`, `e`,`t`, `w` and `i`
int s = ((seven / 10000) % 10);
int v = ((seven / 100) % 10);
if (s != v && s != o && s != n && s != e && s != t && s != w && s != i && v != o && v != n && v != e && v != t && v != w && v != i) {
System.Diagnostics.Trace.WriteLine(seven + "," + nine + "," + one + "," + two);
}
}
}
}
}
}
It seems that nine is always equal to 3136, so that one = 236 and two = 472. However, there are 21 possibiliites for seven. If one adds the constraint that no two digits can take the same value (which is what the C# code above does), then it reduces to just one possibility (although a bug in my code meant this answer originally had 3 possibilities):
seven,nine,one,two
56963,3136,236,472
I just found the time to build a c program to solve your cryptarithm.
I think that tackling the problem mathematicaly, prior to starting the brute force programming, will heavily increase the speed of the output.
Some math (number theory):
Since ONE + ONE = TWO, O cant be larget than 4, because ONE + ONE would result 4 digits. Also O cant be 0. TWO end with O and is an even number, because it is 2 * ONE.
Applying these 3 filters to O, the possible values remain O= {2,4}
Hence E can be {1,2,6,7} because (E+E) modulus 10 must be = O. More specificaly, O=2 implicates E={1,6} and O=4 implicates E={2,7}
Now lets filter N. Given that SEVEN is prime, N must be an odd number. Also N cant be 5, because all that ends with 5 is divisible by 5. Hence N={1,3,7,9}
Now that we have reduced the possibilites for the most ocurring characters (O,E,N), we are ready to hit this cryptarith with all of our brutality, having iterations drastically reduced.
Heres the C code:
#include <stdio.h>
#include <math.h>
#define O 0
#define N 1
#define E 2
#define T 3
#define W 4
#define S 5
#define V 6
#define I 7
bool isPerfectSquare(int number);
bool isPrime(int number);
void printSolutions(int countSolutions);
int filterNoRepeat(int unfilteredCount);
int solutions[1000][8]; // solution holder
int possibilitiesO[2] = {2,4};
int possibilitiesN[4] = {1,3,7,9};
int possibilitiesE[4] = {1,6,2,7};
void main() {
int countSolutions = 0;
int numberOne;
// iterate to fill up the solutions array by: one + one = two
for(int o=0;o<2;o++) {
for(int n=0;n<4;n++) {
for(int e=2*o;e<2*o+2;e++) { // following code is iterated 2*4*2 = 16 times
numberOne = 100*possibilitiesO[o] + 10*possibilitiesN[n] + possibilitiesE[e];
int w = ((2*numberOne)/10)%10;
int t = ((2*numberOne)/100)%10;
// check if NINE is a perfect square
for(int i=0;i<=9;i++) { // i can be anything ----- 10 iterations
int numberNine = 1000*possibilitiesN[n] + 100*i + 10*possibilitiesN[n] + possibilitiesE[e];
if(isPerfectSquare(numberNine)) {
// check if SEVEN is prime
for(int s=1;s<=9;s++) { // s cant be 0 ------ 9 iterations
for(int v=0;v<=9;v++) { // v can be anything other than s ------- 10 iterations
if(v==s) continue;
int numberSeven = 10000*s + 1000*possibilitiesE[e] + 100*v + 10*possibilitiesE[e] + possibilitiesN[n];
if(isPrime(numberSeven)) { // store solution
solutions[countSolutions][O] = possibilitiesO[o];
solutions[countSolutions][N] = possibilitiesN[n];
solutions[countSolutions][E] = possibilitiesE[e];
solutions[countSolutions][T] = t;
solutions[countSolutions][W] = w;
solutions[countSolutions][S] = s;
solutions[countSolutions][V] = v;
solutions[countSolutions][I] = i;
countSolutions++;
}
}
}
}
}
}
}
}
// 16 * 9 * 10 * 10 = 14400 iterations in the WORST scenario, conditions introduced reduce MOST of these iterations to 1 if() line
// iterations consumed by isPrime() function are not taken in count in the aproximation above.
// filter solutions so that no two letter have the same digit
countSolutions = filterNoRepeat(countSolutions);
printSolutions(countSolutions); // voila!
}
bool isPerfectSquare(int number) { // check if given number is a perfect square
double root = sqrt((double)number);
if(root==floor(root)) return true;
else return false;
}
bool isPrime(int number) { // simple algoritm to determine if given number is prime, check interval from sqrt(number) to number/2 with a step of +2
int startValue = sqrt((double)number);
if(startValue%2==0) startValue--; // make it odd
for(int k=startValue;k<number/2;k+=2) {
if(number%k==0) return false;
}
return true;
}
void printSolutions(int countSolutions) {
for(int k=0;k<countSolutions;k++) {
int one = 100*solutions[k][O] + 10*solutions[k][N] + solutions[k][E];
int two = 100*solutions[k][T] + 10*solutions[k][W] + solutions[k][O];
int seven = 10000*solutions[k][S] + 1000*solutions[k][E] + 100*solutions[k][V] + 10*solutions[k][E] + solutions[k][N];
int nine = 1000*solutions[k][N] + 100*solutions[k][I] + 10*solutions[k][N] + solutions[k][E];
printf("ONE: %d, TWO: %d, SEVEN: %d, NINE %d\n",one,two,seven,nine);
}
}
int filterNoRepeat(int unfilteredCount) {
int nrSol = 0;
for(int k=0;k<unfilteredCount;k++) {
bool isValid = true;
for(int i=0;i<7;i++) { // if two letters match, solution is not valid
for(int j=i+1;j<8;j++) {
if(solutions[k][i]==solutions[k][j]) {
isValid = false;
break;
}
}
if(!isValid) break;
}
if(isValid) { // store solution
for(int i=0;i<8;i++) {
solutions[nrSol][i] = solutions[k][i];
}
nrSol++;
}
}
return nrSol;
}
You can try the code yourself if you are still interested in this :P. The result is one single solution: ONE: 236, TWO: 472, SEVEN: 56963, NINE: 3136
This solution is the same as Stochastically's solutions, confirming the correctness of both algorithms i think :).
Thanks for providing this nice cryptarithm and have a nice day!
Brute force FTW!
#define ONE ((o*100) + (n*10) + e)
#define TWO ((t*100) + (w*10) + o)
#define SEVEN ((s*10000) + (e*1010) + (v*100) + n)
#define NINE ((n*1010) + (i*100) + e)
for (o = 1; o < 10; o++) { /* 1st digit cannot be zero (one) */
for (n = 1; n < 10; n++) { /* 1st digit cannot be zero (nine) */
if (n == o) continue;
for (e = 0; n < 10; n++) {
if (e == n) continue;
if (e == o) continue;
/* ... */
if (ONE + ONE == TWO) /* whatever */;
/* ... */
}
}
}

Resources