Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculator #1

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ project(dummy_cmake_project)
set(CMAKE_CXX_STANDARD 14)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

set(SOURCES project.cc)
add_library(ProjectLib ${SOURCES})
set(SOURCES calculator.cc utils.h)
add_library(CalcLib ${SOURCES})

add_executable(project main.cc)
target_link_libraries(project ProjectLib)
add_executable(Calculator main.cc)
target_link_libraries(Calculator CalcLib)

enable_testing()
add_subdirectory(test)
138 changes: 138 additions & 0 deletions calculator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include "calculator.h"
#include <algorithm>
#include <cctype>
#include <numeric>
#include <regex>
#include <vector>
#include "utils.h"


namespace dev {

//void Calculator::save_user_input(const std::string& input) {
// // ** Method checks if input is correct and saves string inserted.
// // ** Shoud be devided into two separate methods
// const std::regex input_pattern(
// R"(^(-?\d*\.?\d*)\s(-?\d*\.?\d*)\s(-?\d*\.?\d*)\s*([\+\-\*\/])$)");
// const bool res = std::regex_search(input, sm_, input_pattern);
// if (res) {
// user_input_ = input;
// }
//}
//
void Calculator::save_user_input(const std::string& input){
if(is_input_correct(input)){
user_input_ = input;
}
else {
user_input_ = "";
// exception here
}
}
void Calculator::process_operands(const std::string& operand,
std::vector<float>& operands) const {
// TODO: probably rename to extract operands
// TODO: use return value instead of output parameter
float current_val = 0; // TODO: Do you need early initialization?
current_val = std::stof(operand);
operands.push_back(current_val);
}




float Calculator::process_operator(const std::string& operation,
std::vector<float>& operands) const {
// I suppose that operator may intract with more that 2 operants
// I would say that it is overengineering for this task

// Can we extract plus, minus , ... to separate functions?

// ** Probably strategy pattern would better to be used instead of switch with inner calculations
// float sum(std::vector<float>& operands){

// }

if ("+" == operation) {
return std::accumulate(operands.cbegin() + 1,
operands.cend(),
operands[0],
std::plus<float>());
}

if ("-" == operation) {
return std::accumulate(operands.cbegin() + 1,
operands.cend(),
operands[0],
std::minus<float>());
}

if ("*" == operation) {
// Good place for switch
return std::accumulate(operands.cbegin() + 1,
operands.cend(),
operands[0],
std::multiplies<float>());
}

if ("/" == operation) {
if (std::any_of(operands.begin() + 1, operands.end(), compare(0))) {
// ** This 'if' doesn't handle math operations itself. Probably should be moved to separate method
// ** inside UI class to make input be checked before calculation
// Looks like check devision by zero.
// Result is 0 ? Any error handling ?
return 0;
}
return std::accumulate(operands.cbegin() + 1,
operands.cend(),
operands[0],
std::divides<float>());
}
// This function is not error safe.
// Please either make it error safe, or create assers in the begining
return 0;
}

// **
bool Calculator::is_input_correct(const std::string &input)
{
const std::regex input_pattern(
R"(^(-?\d*\.?\d*)\s(-?\d*\.?\d*)\s(-?\d*\.?\d*)\s*([\+\-\*\/])$)");
const bool res = std::regex_search(input, sm_, input_pattern);
return res;
}

bool Calculator::is_divident_equals_zero(std::string &operation)
{
if("/" == operation){
return true;
}
return false;
}

float Calculator::calculate_result() const {
float final_res = 0;
std::vector<float> operands;

// auto will make it shorter
for (std::smatch::iterator it = sm_.begin(); it != sm_.end(); ++it) {
const bool is_num = is_number(*it);
// *it value is not obvious, is it regexp search result? lexems?
const bool is_operation = is_operator(*it);

if (is_num) { // additional variables are redundant
process_operands(*it, operands);
// process funciton put *it in operands if *it is operant
// I would give it another name
continue;
}

if (is_operation) {
final_res = process_operator(*it, operands);
// Reassigment final_res in loop is definitely bad idea
}
}
return final_res;
}

} // namespace dev
27 changes: 27 additions & 0 deletions calculator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once
#include "icalculator.h"

#include <regex>
#include <string>

namespace dev {

class Calculator : public ICalculator {
public:
float calculate_result() const override;
void save_user_input(const std::string& input) override;

void process_operands(const std::string& operand,
std::vector<float>& operands) const;
float process_operator(const std::string& operation,
std::vector<float>& operands) const;

private:
bool is_input_correct(const std:: string& input);
bool is_divident_equals_zero(std::string& operation);

// Is there any reasons to have Calculator class ?
std::string user_input_; // TODO :: Looks like user_input used only in setter
std::smatch sm_;
};
} // namespace dev
22 changes: 22 additions & 0 deletions icalculator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <string>

namespace dev {

class ICalculator {

/**
* @brief calculate_result calculates final result
* @return final calculated result
*/
virtual float calculate_result() const = 0;

/**
* @brief save_user_input saves user input inot internal storage
* @param input input to save
*/
virtual void save_user_input(const std::string& input) = 0;
};

} // namespace dev
9 changes: 0 additions & 9 deletions iproject.h

This file was deleted.

24 changes: 23 additions & 1 deletion main.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
#include "project.h"
#include <iostream>
#include <string>
#include "calculator.h"
#include "utils.h"

int main() {
dev::Calculator calc_obj;

// TODO: Make UI (input/output as a separate class or state machine)
std::cout
<< "Input string in format: \"number\" \"space\" \"number\" \"operation\""
<< std::endl;
const std::string user_input = get_user_input();

if (!is_user_input_correct(user_input)) {
std::cout << "Wrong Input!!! Exiting..." << std::endl;

return 0;
}

calc_obj.save_user_input(user_input);

const float final_result = calc_obj.calculate_result();
std::cout << "\nRESULT: " << final_result << std::endl;

return 0;
}
8 changes: 0 additions & 8 deletions project.cc

This file was deleted.

11 changes: 0 additions & 11 deletions project.h

This file was deleted.

6 changes: 3 additions & 3 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ include_directories (${CMAKE_SOURCE_DIR})

include(AddGoogleTest)

add_executable (ProjectTest project_test.cc)
add_gtest(ProjectTest)
target_link_libraries(ProjectTest PUBLIC ProjectLib)
add_executable (CalculatorTest calculator_test.cc utils_test.cc)
add_gtest(CalculatorTest)
target_link_libraries(CalculatorTest PUBLIC CalcLib)
60 changes: 60 additions & 0 deletions test/calculator_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "calculator.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "utils.h"

namespace dev {
namespace testing {

class CalculatorTest : public ::testing::Test {
protected:
dev::Calculator calculator_;
};

TEST_F(CalculatorTest,
CalculateResultOfPlus_CorrectPattern_ExpectCorrectResult) {
const std::string correct_input("3 5 7 +");
ASSERT_TRUE(is_user_input_correct(correct_input));
calculator_.save_user_input(correct_input);
const float correct_result = 15;
EXPECT_EQ(correct_result, calculator_.calculate_result());
}

TEST_F(CalculatorTest,
CalculateResultOfMinus_CorrectPattern_ExpectCorrectResult) {
const std::string input("13 3 7.5 -");
ASSERT_TRUE(is_user_input_correct(input));
calculator_.save_user_input(input);
const float correct_result = 2.5;
EXPECT_EQ(correct_result, calculator_.calculate_result());
}

TEST_F(CalculatorTest,
CalculateResultOfMultiply_CorrectPattern_ExpectCorrectResult) {
const std::string input("15 2 3 *");
ASSERT_TRUE(is_user_input_correct(input));
calculator_.save_user_input(input);
const float correct_result = 90;
EXPECT_EQ(correct_result, calculator_.calculate_result());
}

TEST_F(CalculatorTest,
CalculateResultOfDivision_CorrectPattern_ExpectCorrectResult) {
const std::string input("45 3 5 /");
ASSERT_TRUE(is_user_input_correct(input));
calculator_.save_user_input(input);
const float correct_result = 3;
EXPECT_EQ(correct_result, calculator_.calculate_result());
}

TEST_F(CalculatorTest,
CalculateResultOfDivisionByZero_CorrectPattern_ExpectCorrectResult) {
const std::string input("45 3 0 /");
ASSERT_TRUE(is_user_input_correct(input));
calculator_.save_user_input(input);
const float correct_result = 0;
EXPECT_EQ(correct_result, calculator_.calculate_result());
}

} // namespace testing
} // namespace dev
19 changes: 0 additions & 19 deletions test/project_test.cc

This file was deleted.

10 changes: 10 additions & 0 deletions test/utils_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <gtest/gtest.h>
#include "utils.h"

TEST(UtilsTest, CheckUserInput_WrongPattern_ExpectFalse) {
EXPECT_FALSE(is_user_input_correct(""));
}

TEST(UtilsTest, CheckUserInput_CorrectPattern_ExpectTrue) {
EXPECT_TRUE(is_user_input_correct("3 5 7.2 +"));
}
Loading