Using getchar_unlocked() - c++

I recently learnt that using getchar_unlocked() is a faster way of reading input.
I searched on the internet and found the code snippet below:
But I am unable to understand it.
void fast_scanf(int &number)
{
register int ch = getchar_unlocked();
number= 0;
while (ch > 47 && ch < 58) {
number = number * 10 + ch - 48;
ch = getchar_unlocked();
}
}
int main(void)
{
int test_cases;fast_scanf(test_cases);
while (test_cases--)
{
int n;
fast_scanf(n);
int array[n];
for (int i = 0; i < n; i++)
fast_scanf(array[i]);
}
return 0;
}
So, this code takes input for an integer array of size n for a given number of test_cases . I didn't understand anything in the function fast_scanf, like why this line:
while (ch > 47 && ch < 58)
{ number = number * 10 + ch - 48;
why the register is used while declaring ch?
why the getchar_unlocked() is used twice in the function? and so on..
It would be great help if someone elaborates this for me. Thanks in advance!

Okay, since what you are asking needs to be explained clearly, I am writing it here... so I don't jumble it all up inside the comments...
The function: (Edited it a bit to make it look like more C++-standard)
void fast_scanf(int &number)
{
auto ch = getchar_unlocked();
number= 0;
while (ch >= '0' && ch <= '9')
{
number = number * 10 + ch - '0';
ch = getchar_unlocked();
}
}
Here, take up consideration by looking at the ASCII Table first, since you won't understand how the results are coming up if you don't...
1) Here, you have a character ch takes up the input character from the user using getchar_unlocked() (The auto keyword does that automatically for you and is only usable in C++, not C)...
2) You assign the variable number to zero so that the variable can be re-used, note that the variable is a reference so it changes inside your program as well...
3) while (ch >= '0' && ch <= '9')... As pointed out, checks whether the characters is within the numerical ASCII limit, similar to saying that the character has to be greater than or equal to 48 but less than or equal to 57...
4) Here, things are a little bit tricky, Variable number is multiplied with the product of itself and 10 and the real integer value of the character you stored)...
5) In the next line, ch is reassigned so that you don't have to stay in the loop forever, since ch will remain that number forever if the user doesn't type anything... remember a loop goes back to where it was declared after reaching the end, checks if the condition is true, continues if it is true else breaks)...
For example: 456764
Here, ch will first take 4 then the others follow so we go with 4 first...
1) Number will be assigned to zero. While loop checks if the given character is a number or not, if it is continues the loop else breaks it...
2) Multiplication of 0 with 10 will be zero... and adding it with difference 52 (that is '4') with 48 (that is '0') gives you 4 (the real numerical value, not the char '4')...
So the variable number now is 4...
And the same continues with the others as well... See...
number = number * 10 + '5' - '0'
number = 4 * 10 + 53 - 48
number = 40 + 5
number = 45... etc, etc. for other numbers...

Related

Error with two credit card numbers. Identifies the number as the wrong credit card type

These are my current errors, I think I did something wrong with the maths but everything I tried didn't work.
Ps: Sorry if my question's formatting is bad, first time using stackflow.
:) credit.c exists
:) credit.c compiles
:) identifies 378282246310005 as AMEX
:) identifies 371449635398431 as AMEX
:) identifies 5555555555554444 as MASTERCARD
:) identifies 5105105105105100 as MASTERCARD
:) identifies 4111111111111111 as VISA
:) identifies 4012888888881881 as VISA
:) identifies 4222222222222 as VISA
:) identifies 1234567890 as INVALID
:) identifies 369421438430814 as INVALID
:) identifies 4062901840 as INVALID
:) identifies 5673598276138003 as INVALID
:( identifies 4111111111111113 as INVALID
expected "INVALID\n", not "VISA\n"
:( identifies 4222222222223 as INVALID
expected "INVALID\n", not "VISA\n"
#include <cs50.h>
#include <math.h>
// Prompt user for credit card number
int main(void)
{
long credit_card, credit_number;
do
{
credit_card = get_long("Enter credit card number: ");
}
while (credit_card < 0);
credit_number = credit_card;
// Calculate total number of digits
int count = (credit_number == 0) ? 1 : (log10(credit_number) + 1);
int summation = 0;
while (credit_number == 0)
{
int x = credit_number % 10; summation += x;
int y = 2 * ((credit_number / 10) % 10);
int r = (y % 10) + floor((y / 10) % 10); summation += r; credit_number /= 100;
}
string card;
// Identify which card type you get after inputing your credit card number
int test = cc / pow(10, count - 2);
if ((count == 13 || count == 16) && test / 10 == 4)
{
card = "VISA";
}
else if (count == 16 && test >= 51 && test <= 55)
{
card = "MASTERCARD";
}
else if (count == 15 && (test == 34 || test == 37))
{
card = "AMEX";
}
else
{
card = "INVALID";
}
// Final verification
if (sum % 10 == 0)
{
printf("%s\n", card);
}
else
{
printf("INVALID\n");
}
}```
Your algorithm is maybe not fully correct. I would therefore propose a different approach. You can look at each single digit in a loop. And, you can also do the whole checksum calculation in one step.
I will show you how to do and explain the algorithm behind it.
BTW. Chosing the right algorithm is always the key for success.
So, first we need to think on how we can extract digits from a number. This can be done in a loop by repeating the follwoing steps:
Perform a modulo 10 division to get a digit
Do a integer division by 10
Repeat
Let us look at the example 1234.
Step 1 will get the 4 -- (1234 % 10 = 4)
Step 2 will convert original number into 123 -- (1234 / 10 = 123)
Step 1 will get the 3 -- (123 % 10 = 3)
Step 2 will convert the previous number into 12 -- (123 / 10 = 12)
Step 1 will get the 2 -- (12 % 10 = 2)
Step 2 will convert the previous number into 1 -- (12 / 10 = 1)
Step 1 will get the 1 -- (1 % 10 = 1)
Step 2 will convert the previous number into 0 -- (1 / 10 = 0)
Then the loop stops. Additionally we can observe that the loop stops, when the resulting divided becomes 0. And, we see addtionally that the number of loop executions is equal to the number of digits in the number. But this is somehow obvious.
OK, then let us look, what we learned so far
while (creditCardNumber > 0) {
unsigned int digit = creditCardNumber % 10;
creditCardNumber /= 10;
++countOfDigits;
}
This will get all digits and count them.
Good. Lets go to next step.
For later validation and comparison purpose we need to get the most significant digit (the first digit) and the second most significant digit (the second digit) of the number.
For this, we define 2 variables which will hold the number. We simply assign the current evaluated digit (and override it in each loop execution) to the "mostSignificantDigit". At the end of the loop, we will have it in our desired variable.
For the "secondMostSignificantDigit" we will simple copy the "old" or "previous" value of the "mostSignificantDigit", before assigning a new value to "mostSignificantDigit". With that, we will always have both values available.
The loop looks now like this:
while (creditCardNumber > 0) {
const unsigned int digit = creditCardNumber % 10;
secondMostSignificantDigit = mostSignificantDigit;
mostSignificantDigit = digit;
creditCardNumber /= 10;
++countOfDigits;
}
OK, now we come to the maybe more complex part. The cheksum. The calculation method is.
Start with the least significant (the last) digit
Do not multiply the digit, which is equivalent with multiplying it with 1, and add it to the checksum
Goto the next digit. Multiply it by 2. If the result is greater than 10, then get again the single digits and add both digits to the checksum
Repeat
So, the secret is, to analyze the somehow cryptic specification, given here. If we start with the last digit, we do not multiply it, the next digit will be multiplied, the next not and so on and so on.
To "not multiply" is the same as multiplying by 1. This means: In the loop we need to multiply alternating with 1 or with 2.
How to get alternating numbers in a loop? The algorithm for that is fairly simple. If you need alternating numbers, lets say, x,y,x,y,x,y,x..., Then, build the sum of x and y and perform the subtratcion "value = sum - value". Example:
We need alternating values 1 and 2. The sum is 3. To get the next value, we subtract the current value from the sum.
initial value = 1
sum = 3
current value = initial value = 1
next value = 3 - 1 = 2. Current value = 2
next value = 3 - 2 = 1. Current value = 1
next value = 3 - 1 = 2. Current value = 2
next value = 3 - 2 = 1. Current value = 1
next value = 3 - 1 = 2. Current value = 2
next value = 3 - 2 = 1. Current value = 1
. . .
Good, now we understand, how to make alternating values.
Next, If we multiply a digit with 2, then the maximum result maybe a 2 digit value. We get the single digits with a modulo and an integer division by 10.
And, now important, it does not matter, if we multiply or not, because, if we do not multiply, then the upper digit will always be 0. And this will not contribute to the sum.
With all that, we can always do a multiplication and always split the result into 2 digits (many of them having the upper digit 0).
The result will be:
checkSum += (digit * multiplier) % 10 + (digit * multiplier) / 10;
multiplier = 3 - multiplier;
An astonishingly simple formula.
Next, if we know C or C++ we also know that a multiplication with 2 can be done very efficiently with a bit shift left. And, additionally, a "no-multiplication" can be done with a bit shift 0. That is extremely efficient and faster than multiplication.
x * 1 is identical with x << 0
x * 2 is identical with x << 1
For the final result we will use this mechanism, alternate the multiplier between 0 and 1 and do shifts.
This will give us a very effective checksum calculation.
At the end of the program, we will use all gathered values and compare them to the specification.
Thsi will lead to:
int main() {
// Get the credit card number. Unfortunately I do not know CS50. I use the C++ standard iostream lib.
// Please replace the following 4 lines with your CS50 equivalent
unsigned long long creditCardNumber;
std::cout << "Enter credit card number: ";
std::cin >> creditCardNumber;
std::cout << "\n\n";
// We need to count the number of digits for validation
unsigned int countOfDigits = 0;
// Here we will calculate the checksum
unsigned int checkSum = 0;
// We need to multiply digits with 1 or with 2
unsigned int multiplier = 0;
// For validation purposes we need the most significant 2 digits
unsigned int mostSignificantDigit = 0;
unsigned int secondMostSignificantDigit = 0;
// Now we get all digits from the credit card number in a loop
while (creditCardNumber > 0) {
// Get the least significant digits (for 1234 it will be 4)
const unsigned int digit = creditCardNumber % 10;
// Now we have one digit more. In the end we will have the number of all digits
++countOfDigits;
// Simply remember the most significant digits
secondMostSignificantDigit = mostSignificantDigit;
mostSignificantDigit = digit;
// Calculate the checksum
checkSum += (digit << multiplier) % 10 + (digit << multiplier) / 10;
// Multiplier for next loop
multiplier = 1 - multiplier;
creditCardNumber /= 10;
}
// Get the least significant digit of the checksum
checkSum %= 10;
// Validate all calculated values and show the result
if ((0 == checkSum) && // Checksum must be correct AND
(15 == countOfDigits) && // Count of digits must be correct AND
((3 == mostSignificantDigit) && // Most significant digits must be correct
((4 == secondMostSignificantDigit) || (7 == secondMostSignificantDigit)))) {
std::cout << "AMEX\n";
}
else if ((0 == checkSum) && // Checksum must be correct AND
(16 == countOfDigits) && // Count of digits must be correct AND
((5 == mostSignificantDigit) && // Most significant digits must be correct
((secondMostSignificantDigit > 0) && (secondMostSignificantDigit < 6)))) {
std::cout << "MASTERCARD\n";
}
else if ((0 == checkSum) && // Checksum must be correct AND
((16 == countOfDigits) || (13 == countOfDigits)) && // Count of digits must be correct AND
((4 == mostSignificantDigit))) { // Most significant digit must be correct
std::cout << "VISA\n";
}
else {
std::cout << "INVALID\n";
}
return 0;
}
What we learn with this example, is integer division and modulo division and the smart usage of the identity element for binary operations.
In case of questions, please ask
Just to be complete, I will show you a C++ solution, based on a std::string and using modern C++ elements and algorithms.
For example, the whole checksum calculation will be done with one statement. The whole program does not contain any loop.
#include <iostream>
#include <string>
#include <regex>
#include <numeric>
int main() {
// ---------------------------------------------------------------------------------------------------
// Get user input
// Inform user, what to do. Enter a credit card number. We are a little tolerant with the input format
std::cout << "\nPlease enter a credit card number:\t";
// Get the number, in any format from the user
std::string creditCardNumber{};
std::getline(std::cin, creditCardNumber);
// Remove the noise, meaning, all non digits from the credit card number
creditCardNumber = std::regex_replace(creditCardNumber, std::regex(R"(\D)"), "");
// ---------------------------------------------------------------------------------------------------
// Calculate checksum
unsigned int checksum = std::accumulate(creditCardNumber.rbegin(), creditCardNumber.rend(), 0U,
[multiplier = 1U](const unsigned int sum, const char digit) mutable -> unsigned int {
multiplier = 1 - multiplier; unsigned int value = digit - '0';
return sum + ((value << multiplier) % 10) + ((value << multiplier) / 10); });
// We are only interested in the lowest digit
checksum %= 10;
// ---------------------------------------------------------------------------------------------------
// Validation and output
if ((0 == checksum) && // Checksum must be correct AND
(15 == creditCardNumber.length()) && // Count of digits must be correct AND
(('3' == creditCardNumber[0]) && // Most significant digits must be correct
(('4' == creditCardNumber[1]) || ('7' == creditCardNumber[1])))) {
std::cout << "AMEX\n";
}
else if ((0 == checksum) && // Checksum must be correct AND
(16 == creditCardNumber.length()) && // Count of digits must be correct AND
(('5' == creditCardNumber[0]) && // Most significant digits must be correct
((creditCardNumber[1] > '0') && (creditCardNumber[1] < '6')))) {
std::cout << "MASTERCARD\n";
}
else if ((0 == checksum) && // Checksum must be correct AND
((16 == creditCardNumber.length()) || (13 == creditCardNumber.length())) && // Count of digits must be correct AND
(('4' == creditCardNumber[0]))) { // Most significant digit must be correct
std::cout << "VISA\n";
}
else {
std::cout << "INVALID\n";
}
return 0;

my run-length encoding doesn't work with big numbers

I have a assingment were I need to code and decode txt files, for example: hello how are you? has to be coded as hel2o how are you? and aaaaaaaaaajkle as a10jkle.
while ( ! invoer.eof ( ) ) {
if (kar >= '0' && kar <= '9') {
counter = kar-48;
while (counter > 1){
uitvoer.put(vorigeKar);
counter--;
}
}else if (kar == '/'){
kar = invoer.get();
uitvoer.put(kar);
}else{
uitvoer.put(kar);
}
vorigeKar = kar;
kar = invoer.get ( );
}
but the problem I have is if need to decode a12bhr, the answer is aaaaaaaaaaaabhr but I can't seem to get the 12 as number without problems, I also can't use any strings or array's.
c++
I believe that you are making following mistake: imagine you give a32, then you read the character a and save it as vorigeKar (previous character, I am , Flemish so I understand Dutch :-) ).
Then you read 3, you understand that it is a number and you repeat vorigeKar three times, which leads to aaa. Then you read 2 and repeat vorigeKar two times, leading to aaaaa (five times, five equals 3 + 2).
You need to learn how to keep on reading numeric characters, and translate them into complete numbers (like 32, or 12 in your case).
Like #Dominique said in his answers, You're doing it wrong.
Let me tell you my logic, you can try it.
Pesudo Code + Logic:
Store word as a char array or string, so that it'll be easy to print at last
Loop{
Read - a //check if it's number by subtracting from '0'
Read - 1 //check if number = true. Store it in int res[] = res*10 + 1
//Also store the previous index in an index array(ie) index of char 'a' if you encounter a number first time.
Read - 2 //check if number = true. Store it in res = res*10 + 2
Read - b , h and so on till "space" character
If you encounter another number, then store it's previous character's index in index array and then store the number in a res[] array.
Now using index array you can get the index of your repeating character to be printed and print it for it's corresponding times which we have stored in the result array.
This goes for the second, third...etc:- numbers in your word till the end of the word
}
First, even though you say you can't use strings, you still need to know the basic principle behind how to turn a stream of digit characters into an integer.
Assuming the number is positive, here is a simple function that turns a series of digits into a number:
#include <iostream>
#include <cctype>
int runningTotal(char ch, int lastNum)
{
return lastNum * 10 + (ch -'0');
}
int main()
{
// As a test
char s[] = "a123b23cd1/";
int totalNumber = 0;
for (size_t i = 0; s[i] != '/'; ++i)
{
char digit = s[i]; // This is the character "read from the file"
if ( isdigit( digit) )
totalNumber = runningTotal(digit, totalNumber);
else
{
if ( totalNumber > 0 )
std::cout << totalNumber << "\n";
totalNumber = 0;
}
}
std::cout << totalNumber;
}
Output:
123
23
1
So what was done? The character array is the "file". I then loop for each character, building up the number. The runningTotal is a function that builds the integer from each digit character encountered. When a non-digit is found, we output that number and start the total from 0 again.
The code does not save the letter to "multiply" -- I leave that to you as homework. But the code above illustrates how to take digits and create the number from them. For using a file, you would simply replace the for loop with the reading of each character from the file.

Specify a range of ASCII lowercase chars in C++

I am writing a program that takes a char and compares it to see if it's in a range of certain chars. For instance, if the char I get is an n I go to state 3, if its a - m or o - z I go to state 4. I'm new to C++ so I'm still learning.
Can I say something like:
char c = file.next_char();
...
if (c in 'a'...'m', 'o'...'z')
{
state = 3;
} else {
state = 4;
}
There is no such syntax in C++. The options are:
Use a switch statement, when the list of values is generally not contiguous, or
Convert the list of explicit character values into contiguous ranges into equivalent boolean expressions. As you know, alphabetic characters consist of a contiguous range of octets in ASCII, so your pseudo-code is equivalent to:
if ( (c >= 'a' && c <= 'm')
||
(c >= 'o' && c <= 'z'))
If you are using ascii (English), you can rely on the fact that all the lower case letters are adjacent. Just check 'a' <= c && c <= 'z'
after ruling out 'n'.
You never said what happens if the state is not one of those, so I left it alone.
// 3 and 4 mean nothing. Give your states meaningful names
enum state_type {FirstState, SecondState, ThirdState, FourthState};
state_type state = FirstState;
char c = get_next_char();
if ('n' == c){
state = FourthState;
} else if ('a' < c && c < 'z'){
state = ThirdState;
} else {
// no change?
}
You could maybe use a for loop to compare it to see if it's a letter in the first or second range. The code would be something this:
char range1[/*amount in array here*/] = "abcdefghijklmnopqrstuvwxyz";
//Substitute range on line above(the characters in the string)
for(int i = 0; i <= /*amount in array here*/; i++) {
if(range1[i] == /*nameoflettervariablehere*/){
//Code here
}
}
I'm sorry but I don't know of a more efficient way.

Homemade Vigenere cipher; working with acsii character manipulation

As the post title suggests, I'm working to strengthen my grasp on C++ and character manipulation, this time through creating a Vigenere Cipher. For those unfamiliar with it, it's a fairly simple way to encrypt a text file.
The basic way it works is that there exists a string "key", and each character (in my case at least) is a lowercase alphabetical character. These are stored into an array and are used to "shift" the value of the file being encoded. A character of 'a' will shift the target by 0, while 'z' will shift it by 25. The "shift" is cyclical, meaning that if 'z' is shifted by 'b' (1) it should result in an 'a'.
My current method is found below:
//Assume cipher[] contains "[a][b][c][x ][y ][z ]" Cipher is a <string> object
//Assume ptr[] contains "[0][1][2][23][24][25]
#A whole bunch of includes
char c;
ifstream is;
ofstream os;
is.open(argv[3]) //"myinput.txt"
os.open(argv[4]) //"myoutput.txt"
int i = 0;
while( is.good() ) {
c = is.get();
if( is.good() ) { //did we just hit the EoF?
c = tolower( c - 0 ); //just make sure it's lowercase
c = c + ptr[ i % cipher.size() ] % 26;
if( c> 122 )
c = ( c % 123 ) + 97;
i++;
os.put( c );
}
}
My problem lies in my modulo operations, I believe. Maybe it's because I've spent so much time hashing this out, but I spent hours last night writing this, and then another hour lying in bed trying to wrap my mind around how to effectively create what I want:
grab char.
check char. //let char = 'z'
check the cipher. //let the cipher = 'y'
eval cipher shift //'y' shift value = 24
shift z 24 places (cyclically) //'z'==25, 25+24=49, 49%26=23. 23='x'
HERE IS THE ISSUE: How to do this with ACSII? ('a'=97, z='121')
Imagine that you want to "shuffle" the "ones" digits 0-9 between 20 and 29 by two steps, such that 20 becomes 22, and 29 becomes 21,. How would you do that?
Well, I would subtract 20 [our base number], and then shuffle the remaining digit, and then add 20 back in again.
newnum = num - 20;
newnum %= 10;
newnum += 20;
The same principle would apply for ascii - just that of course the base isn't 20.

Caesar cipher in C++

To start off, I'm four weeks into a C++ course and I don't even know loops yet, so please speak baby talk?
Okay, so I'm supposed to read a twelve character string (plus NULL makes thirteen) from a file, and then shift the letters backwards three, and then print my results to screen and file. I'm okay with everything except the shifting letters. I don't want to write miles of code to take each character individually, subtract three, and re-assemble the string, but I'm not sure how to work with the whole string at once. Can someone recommend a really simple method of doing this?
If you are dealing with simple letters (A to Z or a to z), then you can assume that the internals codes are linear.
Letters are coded as numbers, between 0 and 127. A is coded as 65, B as 66, C as 67, Z as 90.
In order to shift letters, you just have to change the internal letter code as if it were a number, so basically just substracting 3 from the character. Beware of edge cases though, because substracting 3 to 'A' will give you '>' (code 62) and not 'X' (code 88). You can deal with them using "if" statements or the modulo operator ("%").
Here is an ASCII characters table to help you
Once you've loaded your string in, you can use the modulous operator to rotate while keeping within the confines of A-Z space.
I'd keep track of whether the letter was a capital to start with:
bool isCaps = ( letter >= 'A' ) && ( letter <= 'Z' );
if( isCaps )
letter -= 'A'-'a';
and then just do the cipher shift like this:
int shift = -3;
letter -= 'a'; // to make it a number from 0-25
letter = ( letter + shift + 26 ) % 26;
// add 26 in case the shift is negative
letter += 'a'; // back to ascii code
finally finish off with
if( isCaps )
letter += 'A'-'a';
so, putting all this together we get:
char *mystring; // ciphertext
int shift = -3; // ciphershift
for( char *letter = mystring; letter; ++letter )
{
bool isCaps = ( *letter >= 'A' ) && ( *letter <= 'Z' );
if( isCaps )
*letter -= 'A'-'a';
letter -= 'a';
letter = ( letter + shift + 26 ) % 26;
letter += 'a';
if( isCaps )
letter += 'A'-'a';
}
You're going to have to learn loops. They will allow you to repeat some code over the characters of a string, which is exactly what you need here. You'll keep an integer variable that will be your index into the string, and inside the loop do your letter-shifting on the character at that index and increment the index variable by one until you reach NULL.
Edit: If you're not expected to know about loops yet in your course, maybe they want you to do this:
string[0] -= 3; // this is short for "string[0] = string[0] - 3;"
string[1] -= 3;
string[2] -= 3;
...
It will only result in 12 lines of code rather than miles. You don't have to "reassemble" the string this way, you can just edit each character in-place. Then I bet after making you do that, they'll show you the fast way of doing it using loops.
Iterate over the characters with a for loop. And do what you want with the char*. Then put the new char back.
for(int i=0; i<12; i++){
string[i] = string[i] - 3;
}
Where string is your character array (string). There is a bit more involved if you want to make it periodic (I.E. have A wrap round to Z, but the above code should help you get started)
I'm a little unclear what you mean by "shift the letters backwards 3"?
Does that mean D ==> A?
If so, here's a simple loop.
(I didn't do reading from the file, or writing to the file... Thats your part)
#include <string.h>
int main(void)
{
char input[13] = "ABCDEFGHIJKL";
int i;
int len = strlen(input);
for(i=0; i<len; ++i)
{
input[i] = input[i]-3;
}
printf("%s", input); // OUTPUT is: ">?#ABCDEFGHI"
}