correctcppis_perfect

March 2, 2018 ยท View on GitHub

BranchTravis CICodecov
masterBuild Statuscodecov.io

Correct C++ chapter 'is_perfect'.

Goal

  • Refactor an application to lower cyclomatic complexity

Prerequisites

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_perfectOutputExit status
./is_perfectAny1
./is_perfect X, where X is a number that is not perfectfalse (with newline)0
./is_perfect Y, where Y is a number that is perfecttrue (with newline)0
./is_perfect nonsenseAny1
./is_perfect 6 28Any1

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
NumberIs perfect?
Less than 6No
6Yes
7 to an including 27No
28Yes