From 87fc9fba010fa108fbeb49acf202a240154c7867 Mon Sep 17 00:00:00 2001 From: Andy Zhang Date: Mon, 3 Jun 2024 22:47:10 +0800 Subject: [PATCH] Added `factorial`. --- CMakeLists.txt | 1 + include/fn/basicArithm.hpp | 10 +++ src/factorial/factorial.cpp | 112 ++++++++++++++++++++++++++++++ src/factorial/factorialReport.cpp | 48 +++++++++++++ src/factorial/factorialReport.hpp | 35 ++++++++++ tests/testFactorial.cpp | 38 ++++++++++ 6 files changed, 244 insertions(+) create mode 100644 src/factorial/factorial.cpp create mode 100644 src/factorial/factorialReport.cpp create mode 100644 src/factorial/factorialReport.hpp create mode 100644 tests/testFactorial.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7051f624d..dde61a504 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ SET(COMPONENTS power division root + factorial ) # NEW_COMPONENT: PATCH Do NOT remove the previous comment. diff --git a/include/fn/basicArithm.hpp b/include/fn/basicArithm.hpp index 0e5cf10c7..8d1e62f70 100644 --- a/include/fn/basicArithm.hpp +++ b/include/fn/basicArithm.hpp @@ -212,6 +212,16 @@ namespace steppable::__internals::arithmetic */ std::string rootIntPart(const std::string& _number, const std::string& base); + /** + * @brief Calculates the factorial of a number. + * + * @param _number The number to calculate the factorial of. + * @param steps The number of steps to calculate the factorial. + * + * @return The factorial of the number. + */ + std::string factorial(const std::string& _number, int steps = 2); + /** * @brief Executes a given predicate function a specified number of times. * diff --git a/src/factorial/factorial.cpp b/src/factorial/factorial.cpp new file mode 100644 index 000000000..66d04ea53 --- /dev/null +++ b/src/factorial/factorial.cpp @@ -0,0 +1,112 @@ +/************************************************************************************************** + * Copyright (c) 2023-2024 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +/** + * @file add.cpp + * @brief This file contains the implementation of the add function, which add two number strings together. + * + * @author Andy Zhang + * @date 9th October 2023 + */ + +#include "argParse.hpp" +#include "factorialReport.hpp" +#include "fn/basicArithm.hpp" +#include "output.hpp" +#include "util.hpp" + +#include +#include +#include +#include + +using namespace steppable::__internals::numUtils; +using namespace steppable::__internals::utils; +using namespace steppable::__internals::arithmetic; +using namespace steppable::__internals::symbols; +using namespace steppable::output; +using namespace std::literals; + +namespace steppable::__internals::arithmetic +{ + std::string factorial(const std::string& _number, const int steps) + { + auto number = standardizeNumber(_number); + if (not isInteger(number)) + { + // We cannot evaluate integrals yet, so this has to be returned. + // Correct implementation of the gamma function should be + // / +inf + // | z-1 -s + // G(z) = | s e ds ,where z is a non-zero decimal ............ (1) + // | + // / 0 + // and factorial is defined as + // n! = n * G(n) ,where n is a non-zero decimal ............ (2) + error("factorial"s, "%s is not an integer."s, number.c_str()); + return "0"; + } + if (isZeroString(number)) + { + if (steps == 2) + return "By definition, 0! = 1"; + return "1"; // By definition, 0! = 1 + } + // Negative numbers do not have a factorial. + if (number.front() == '-') + { + error("factorial"s, "%s is negative."s, number.c_str()); + return "0"; + } + + // Calculate the factorial of the number + std::string result = "1"; + loop(_number, [&](const std::string& i) { result = multiply(result, add(i, "1", 0), 0); }); + + return reportFactorial(_number, result, steps); + } +} // namespace steppable::__internals::arithmetic + +#ifndef NO_MAIN +int main(const int _argc, const char* _argv[]) +{ + Utf8CodePage _; + ProgramArgs program(_argc, _argv); + program.addPosArg('a', "Number"); + program.addKeywordArg("steps", 2, "Amount of steps while calculating the factorial. 0 = No steps, 2 = All steps."); + program.addSwitch("profile", false, "profiling the program"); + program.parseArgs(); + + const int steps = program.getKeywordArgument("steps"); + const bool profile = program.getSwitch("profile"); + const auto& number = static_cast(program.getPosArg(0)); + + if (profile) + { + TIC(Factorial) + std::cout << "Factorial :\n" << factorial(number, steps) << '\n'; + TOC() + } + else + std::cout << factorial(number, steps) << '\n'; +} +#endif diff --git a/src/factorial/factorialReport.cpp b/src/factorial/factorialReport.cpp new file mode 100644 index 000000000..76831f10c --- /dev/null +++ b/src/factorial/factorialReport.cpp @@ -0,0 +1,48 @@ +/************************************************************************************************** + * Copyright (c) 2023-2024 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#include "factorialReport.hpp" + +#include "fn/basicArithm.hpp" +#include "symbols.hpp" + +#include +#include + +using namespace steppable::__internals::arithmetic; +using namespace steppable::__internals::symbols; + +std::string reportFactorial(const std::string& number, const std::string& result, int steps) +{ + if (steps == 0) + return result; + + // In this case, steps == 2 is equivalent to steps == 1. + std::stringstream ss; + loop(number, [&](const auto& item) { ss << add(item, "1", 0) << " " << MULTIPLY << " "; }); + auto out = ss.str(); + + out.erase(out.end() - 3, out.end()); // Remove the last " * " + out += " = " + result; + + return out; +} diff --git a/src/factorial/factorialReport.hpp b/src/factorial/factorialReport.hpp new file mode 100644 index 000000000..6cc119892 --- /dev/null +++ b/src/factorial/factorialReport.hpp @@ -0,0 +1,35 @@ +/************************************************************************************************** + * Copyright (c) 2023-2024 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#pragma once + +#include + +/** + * @brief Report the factorial calculation. + * + * @param number The number to calculate the factorial of. + * @param result The result of the factorial calculation. + * @param steps The number of steps taken to calculate the factorial. + * @return std::string The report of the factorial calculation. + */ +std::string reportFactorial(const std::string& number, const std::string& result, int steps); diff --git a/tests/testFactorial.cpp b/tests/testFactorial.cpp new file mode 100644 index 000000000..303f59ef8 --- /dev/null +++ b/tests/testFactorial.cpp @@ -0,0 +1,38 @@ +/************************************************************************************************** + * Copyright (c) 2023-2024 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#include "colors.hpp" +#include "fn/basicArithm.hpp" +#include "output.hpp" +#include "testing.hpp" +#include "util.hpp" + +#include +#include + +using namespace steppable::__internals::arithmetic; + +TEST_START() +SECTION(Factorial) +_.assertIsEqual(factorial("5", 0), "120"); +SECTION_END() +TEST_END()