Last active
December 15, 2015 03:59
-
-
Save maxdeliso/5198856 to your computer and use it in GitHub Desktop.
non-negative integer recognizer in C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * A practical implementation of a 'bulletproof' non-negative integer | |
| * recognizer with overflow detection. Results are written to stdout, | |
| * and diagnostics are written to stderr. Using redirection, you can | |
| * easily retrieve just the recognized values separated by newlines | |
| * like this: nnIntRecognizer <testInput >testOutput | |
| * | |
| * some edge cases ferreted in cooperating with Jared | |
| */ | |
| #include <iostream> | |
| #include <algorithm> | |
| #include <vector> | |
| #include <cctype> | |
| #include <cassert> | |
| #include <cmath> | |
| int ASCII2Int(const char ch); | |
| int computeSum(std::vector<int> const & digits); | |
| int tenPow(int p); | |
| int main() { | |
| /* mutable state */ | |
| std::vector<int> currentDigits; | |
| char thisChar; | |
| /* input loop */ | |
| while(std::cin >> thisChar) { | |
| const bool inNumber = currentDigits.size() > 0; | |
| if(isdigit(thisChar)) { | |
| const int thisDigit = ASCII2Int(thisChar); | |
| if(inNumber) { | |
| /* in number, got another digit => continuation of number */ | |
| const int currentSum = computeSum(currentDigits); | |
| std::cerr << "\ncontinuation of number...\n" | |
| << "\n running total before this number: " | |
| << currentSum << std::endl; | |
| /* append digit to vector */ | |
| currentDigits.push_back(thisDigit); | |
| const int nextSum = computeSum(currentDigits); | |
| /* check to see if the next digit causes an overflow */ | |
| if(nextSum < 0) { | |
| currentDigits.pop_back(); | |
| std::cout << currentSum << std::endl; | |
| std::cerr << "\noverflow! starting new number\n"; | |
| /* digit that caused overflow => the first digit in the next number */ | |
| currentDigits.clear(); | |
| currentDigits.push_back(thisDigit); | |
| } else { | |
| std::cerr << "\n running total after this number: " | |
| << nextSum << std::endl; | |
| } | |
| } else { | |
| /* not in number, got a digit => start of number */ | |
| std::cerr << "\nstart of number...\n"; | |
| currentDigits.push_back(thisDigit); | |
| } | |
| } else { | |
| if(inNumber) { | |
| /* in number, got non-digit => end of number*/ | |
| const int finalSum = computeSum(currentDigits); | |
| std::cout << finalSum << std::endl; | |
| std::cerr << "\nend of number...\n"; | |
| currentDigits.clear(); | |
| } else { | |
| /* not in number, got a non-digit => ignore it */ | |
| std::cerr << "\nignoring non-consecutive non-digits...\n"; | |
| } | |
| } | |
| } | |
| std::cerr << "\ngot EOF, preparing to exit\n"; | |
| /* there may still be digits when the EOF is encountered, so handle these */ | |
| if(currentDigits.size() > 0) { | |
| std::cerr << "\nprocessing remaining digits at EOF\n"; | |
| const int finalSum = computeSum(currentDigits); | |
| std::cout << finalSum << std::endl; | |
| } | |
| return 0; | |
| } | |
| int ASCII2Int(const char ch) { | |
| assert(isdigit(ch)); | |
| int n = ch - '0'; | |
| assert(n >= 0 && n <= 9); | |
| return n; | |
| } | |
| int computeSum(std::vector<int> const & digits) { | |
| static const int maxP = floor(log10(INT_MAX)); | |
| static const int maxTenPow = tenPow(maxP); | |
| static const int maxLeadDigit = floor(INT_MAX / maxTenPow); | |
| int tensPos = 0; | |
| int sum = 0; | |
| /* remove leading zeroes from digits vector and store the result in | |
| * processed digits */ | |
| unsigned int digitsToIgnore = 0; | |
| while(digitsToIgnore < digits.size() && digits[digitsToIgnore] == 0) { | |
| ++digitsToIgnore; | |
| } | |
| std::vector<int> processedDigits(digits.begin() + digitsToIgnore, | |
| digits.end()); | |
| /* counting from ones place to the most significant place (right to left) */ | |
| for(std::vector<int>::const_reverse_iterator cri = processedDigits.rbegin(); | |
| cri != processedDigits.rend(); | |
| ++cri) { | |
| const int currentTenMult = tenPow(tensPos); | |
| if(currentTenMult < 0) { | |
| return -1; | |
| } | |
| /* check for edge case where multiplier can be represented but the | |
| * product can not */ | |
| if( tensPos == maxP && (*cri) > maxLeadDigit ) { | |
| return -1; | |
| } | |
| const int increment = (*cri) * currentTenMult; | |
| /* we don't handle negative signs in this program, so this is easier; | |
| * we can just check to see if our sum is less than 0. if it is return | |
| * -1 to signal overflow */ | |
| if(sum + increment < 0) { | |
| return -1; | |
| } | |
| sum += increment; | |
| ++tensPos; | |
| } | |
| return sum; | |
| } | |
| int tenPow(int p) { | |
| static const int maxP = floor(log10(INT_MAX)); | |
| assert( p >= 0); | |
| if( p > maxP) { | |
| return -1; | |
| } | |
| int result = 1; | |
| for (int j = 1; j <= p; ++j) { | |
| result *= 10; | |
| } | |
| return result; | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
in>
0000000000000000000000000000000,
00000000000000000000000000010,
9999999999999999999999999999999999999999999,
123,
321,
0,
1,
0,
010,
100,
010,
001
out>
0
10
999999999
999999999
999999999
999999999
9999999
123
321
0
1
0
10
100
10
1