Awesome
correct_cpp_is_perfect
Branch | Travis CI | Codecov |
---|---|---|
master |
Correct C++ chapter 'is_perfect'.
Goal
- Refactor an application to lower cyclomatic complexity
Prerequisites
- Understand how this course works
- Have some experience how to lower cyclomatic complexity
- Have written a correct 'is_prime' program
Exercise
Write a command-line interface (CLI) program that determines if a number is a perfect number (see 'What is a perfect number?').
Here are the outputs and exit statuses the program should give:
Call to is_perfect | Output | Exit status |
---|---|---|
./is_perfect | Any | 1 |
./is_perfect X , where X is a number that is not perfect | false (with newline) | 0 |
./is_perfect Y , where Y is a number that is perfect | true (with newline) | 0 |
./is_perfect nonsense | Any | 1 |
./is_perfect 6 28 | Any | 1 |
In this exercise, you start with the code below. Yes, that code works perfectly.
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char* argv[])
{
if (argc != 2) return 1;
try
{
const int value{std::stoi(argv[1])};
// Is this a perfect number?
// -1: unknown
// 0: false
// 1: true
int is_perfect{-1};
// Negative values are not perfect
if (value < 0) is_perfect = 0;
// Zero is not perfect
if (is_perfect == -1 && value == 0) is_perfect = 0;
//Collect the proper divisors
std::vector<int> proper_divisors;
if (is_perfect == -1 && value == 2)
{
proper_divisors.push_back(1);
}
else if (is_perfect == -1 && value > 2)
{
for (int denominator=1; denominator!=value-1; ++denominator)
{
if (value % denominator == 0)
{
proper_divisors.push_back(denominator);
}
}
}
//sum the proper divisors, if not known if number is perfect
int sum{0};
if (is_perfect == -1)
{
for (const int proper_divisor: proper_divisors) { sum += proper_divisor; }
}
if (is_perfect == -1 && sum == value) is_perfect = 1;
if (is_perfect == -1) is_perfect = 0;
//show
assert(is_perfect != -1); //Must be known now
if (is_perfect == 1)
{
std::cout << "true\n";
}
else
{
std::cout << "false\n";
}
}
catch (const std::exception&)
{
return 1;
}
}
- You may start from scratch if you think that is simpler
- The code has a too high cyclomatic complexity. Simplify it. See how to lower cyclomatic complexity.
Tips:
- the comments tell what is happening, create functions with those names
- a possible function prototype:
std::vector<int> collect_proper_divisors(const int i) noexcept
- a possible function prototype:
int sum(const std::vector<int>& v) noexcept
- a possible function prototype:
bool is_perfect(const int i) noexcept
- Your code needs to have 100% code coverage, regardless how it is called (that is, with zero, one or more arguments), see how to get 100 percent code coverage
What is a perfect number?
Any number is a perfect number if the sum of its proper divisors equals itself. A number's divisors are those value the number can be divided by without leaving a remainer. A number's proper divisors are those divisors different from the number itself.
For example:
- 6 has divisors 1,2,3 and 6
- 6 has proper divisors 1,2 and 3
- 6 is perfect, as 1+2+3=6
Number | Is perfect? |
---|---|
Less than 6 | No |
6 | Yes |
7 to an including 27 | No |
28 | Yes |