diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000000..55cadc285f2 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @Panquesito7 @realstealthninja diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f8135639907..268098beecd 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,7 +3,7 @@ Thank you for your Pull Request. Please provide a description above and review the requirements below. -Contributors guide: https://github.com/TheAlgorithms/C-Plus-Plus/CONTRIBUTING.md +Contributors guide: https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/CONTRIBUTING.md --> #### Checklist @@ -18,4 +18,4 @@ Contributors guide: https://github.com/TheAlgorithms/C-Plus-Plus/CONTRIBUTING.md - [ ] Search previous suggestions before making a new one, as yours may be a duplicate. - [ ] I acknowledge that all my contributions will be made under the project's license. -Notes: \ No newline at end of file +Notes: diff --git a/.github/workflows/awesome_workflow.yml b/.github/workflows/awesome_workflow.yml index c4c398cd989..32555bbbd29 100644 --- a/.github/workflows/awesome_workflow.yml +++ b/.github/workflows/awesome_workflow.yml @@ -27,11 +27,6 @@ jobs: wget https://raw.githubusercontent.com/TheAlgorithms/scripts/main/filename_formatter.sh chmod +x filename_formatter.sh ./filename_formatter.sh . .cpp,.hpp - - name: Update DIRECTORY.md - run: | - wget https://raw.githubusercontent.com/TheAlgorithms/scripts/main/build_directory_md.py - python3 build_directory_md.py C-Plus-Plus . .cpp,.hpp,.h > DIRECTORY.md - git commit -m "updating DIRECTORY.md" DIRECTORY.md || true - name: Get file changes run: | git branch @@ -43,47 +38,8 @@ jobs: # be able to catch any errors for other platforms. run: cmake -B build -S . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - name: Lint modified files - shell: python - run: | - import os - import subprocess - import sys - - print("Python {}.{}.{}".format(*sys.version_info)) # Python 3.8 - with open("git_diff.txt") as in_file: - modified_files = sorted(in_file.read().splitlines()) - print("{} files were modified.".format(len(modified_files))) - - cpp_exts = tuple(".c .c++ .cc .cpp .cu .cuh .cxx .h .h++ .hh .hpp .hxx".split()) - cpp_files = [file for file in modified_files if file.lower().endswith(cpp_exts)] - print(f"{len(cpp_files)} C++ files were modified.") - if not cpp_files: - sys.exit(0) - - subprocess.run(["clang-tidy", "--fix", "-p=build", "--extra-arg=-std=c++11", *cpp_files, "--"], - check=True, text=True, stderr=subprocess.STDOUT) - - subprocess.run(["clang-format", "-i", "-style=file", *cpp_files], - check=True, text=True, stderr=subprocess.STDOUT) - - upper_files = [file for file in cpp_files if file != file.lower()] - if upper_files: - print(f"{len(upper_files)} files contain uppercase characters:") - print("\n".join(upper_files) + "\n") - - space_files = [file for file in cpp_files if " " in file or "-" in file] - if space_files: - print(f"{len(space_files)} files contain space or dash characters:") - print("\n".join(space_files) + "\n") - - nodir_files = [file for file in cpp_files if file.count(os.sep) != 1] - if nodir_files: - print(f"{len(nodir_files)} files are not in one and only one directory:") - print("\n".join(nodir_files) + "\n") - - bad_files = len(upper_files + space_files + nodir_files) - if bad_files: - sys.exit(bad_files) + shell: bash + run: python3 scripts/file_linter.py - name: Commit and push changes run: | git diff DIRECTORY.md diff --git a/.github/workflows/directory_writer.yml b/.github/workflows/directory_writer.yml new file mode 100644 index 00000000000..48f71505cdf --- /dev/null +++ b/.github/workflows/directory_writer.yml @@ -0,0 +1,28 @@ +name: Directory writer +on: + schedule: + # ┌───────────── minute (0 - 59) + # │ ┌───────────── hour (0 - 23) + # │ │ ┌───────────── day of the month (1 - 31) + # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) + # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) + # │ │ │ │ │ + # │ │ │ │ │ + # │ │ │ │ │ + # * * * * * + - cron: '0 0 * * *' +jobs: + build: + if: github.repository == 'TheAlgorithms/C-Plus-Plus' # We only need this to run in our repository. + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Build directory + uses: TheAlgorithms/scripts/directory_md@main + with: + language: C-Plus-Plus + working-directory: . + filetypes: .cpp,.hpp,.h + ignored-directories: doc/ diff --git a/.gitignore b/.gitignore index 5275088d0f4..a94731cc2ed 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ a.out *.app build/ +git_diff.txt diff --git a/.vscode/settings.json b/.vscode/settings.json index 67fe06477bf..f6d76514ffb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -59,6 +59,33 @@ "stdexcept": "cpp", "streambuf": "cpp", "typeinfo": "cpp", - "valarray": "cpp" + "valarray": "cpp", + "bit": "cpp", + "charconv": "cpp", + "compare": "cpp", + "concepts": "cpp", + "format": "cpp", + "forward_list": "cpp", + "ios": "cpp", + "locale": "cpp", + "queue": "cpp", + "stack": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "climits": "cpp" } } diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bbda35e1ce..cca1b54649b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.22) project(Algorithms_in_C++ LANGUAGES CXX VERSION 1.0.0 @@ -43,6 +43,9 @@ add_subdirectory(machine_learning) add_subdirectory(numerical_methods) add_subdirectory(graph) add_subdirectory(divide_and_conquer) +add_subdirectory(games) +add_subdirectory(cpu_scheduling_algorithms) +add_subdirectory(physics) cmake_policy(SET CMP0054 NEW) cmake_policy(SET CMP0057 NEW) diff --git a/DIRECTORY.md b/DIRECTORY.md index 817fdd89bef..05ad073afcc 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -20,6 +20,7 @@ * [Count Of Trailing Ciphers In Factorial N](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/bit_manipulation/count_of_trailing_ciphers_in_factorial_n.cpp) * [Find Non Repeating Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/bit_manipulation/find_non_repeating_number.cpp) * [Hamming Distance](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/bit_manipulation/hamming_distance.cpp) + * [next higher number with same number of set bits](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/bit_manipulation/next_higher_number_with_same_number_of_set_bits.cpp) * [Power Of 2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/bit_manipulation/power_of_2.cpp) * [Set Kth Bit](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/bit_manipulation/set_kth_bit.cpp) * [Travelling Salesman Using Bit Manipulation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/bit_manipulation/travelling_salesman_using_bit_manipulation.cpp) @@ -120,6 +121,9 @@ * [Tree Height](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/tree_height.cpp) * [Word Break](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/word_break.cpp) +## Games + * [Memory Game](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/games/memory_game.cpp) + ## Geometry * [Graham Scan Algorithm](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/geometry/graham_scan_algorithm.cpp) * [Graham Scan Functions](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/geometry/graham_scan_functions.hpp) @@ -168,6 +172,7 @@ * [Md5](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/hashing/md5.cpp) * [Quadratic Probing Hash Table](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/hashing/quadratic_probing_hash_table.cpp) * [Sha1](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/hashing/sha1.cpp) + * [Sha256](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/hashing/sha256.cpp) ## Machine Learning * [A Star Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/machine_learning/a_star_search.cpp) @@ -289,6 +294,7 @@ * [Happy Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/happy_number.cpp) * [Iterative Tree Traversals](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/iterative_tree_traversals.cpp) * [Kadanes3](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/kadanes3.cpp) + * [Kelvin To Celsius](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/kelvin_to_celsius.cpp) * [Lru Cache](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/lru_cache.cpp) * [Matrix Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/matrix_exponentiation.cpp) * [Palindrome Of Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/palindrome_of_number.cpp) diff --git a/README.md b/README.md index 03093277bc5..5ad49b18473 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ This repository is a collection of open-source implementation of a variety of al * The repository provides implementations of various algorithms in one of the most fundamental general purpose languages - [C++](https://en.wikipedia.org/wiki/C%2B%2B). * Well documented source code with detailed explanations provide a valuable resource for educators and students alike. * Each source code is atomic using [STL classes](https://en.wikipedia.org/wiki/Standard_Template_Library) and _no external libraries_ are required for their compilation and execution. Thus, the fundamentals of the algorithms can be studied in much depth. -* Source codes are [compiled and tested](https://github.com/TheAlgorithms/C-Plus-Plus/actions?query=workflow%3A%22Awesome+CI+Workflow%22) for every commit on the latest versions of three major operating systems viz., Windows, MacOS and Ubuntu (Linux) using MSVC 16 2019, AppleClang 11.0 and GNU 7.5.0 respectively. +* Source codes are [compiled and tested](https://github.com/TheAlgorithms/C-Plus-Plus/actions?query=workflow%3A%22Awesome+CI+Workflow%22) for every commit on the latest versions of three major operating systems viz., Windows, MacOS, and Ubuntu (Linux) using MSVC 19 2022, AppleClang 14.0.0, and GNU 11.3.0 respectively. * Strict adherence to [C++11](https://en.wikipedia.org/wiki/C%2B%2B11) standard ensures portability of code to embedded systems as well like ESP32, ARM Cortex, etc. with little to no changes. * Self-checks within programs ensure correct implementations with confidence. * Modular implementations and OpenSource licensing enable the functions to be utilized conveniently in other applications. diff --git a/bit_manipulation/next_higher_number_with_same_number_of_set_bits.cpp b/bit_manipulation/next_higher_number_with_same_number_of_set_bits.cpp new file mode 100644 index 00000000000..d93884d4e11 --- /dev/null +++ b/bit_manipulation/next_higher_number_with_same_number_of_set_bits.cpp @@ -0,0 +1,100 @@ +/** + * @file + * @brief [Next higher number with same number of set bits] + * (https://www.geeksforgeeks.org/next-higher-number-with-same-number-of-set-bits/) + * implementation + * + * @details + * Given a number x, find next number with same number of 1 bits in it’s binary + * representation. For example, consider x = 12, whose binary representation is + * 1100 (excluding leading zeros on 32 bit machine). It contains two logic 1 + * bits. The next higher number with two logic 1 bits is 17 (100012). + * + * A binary number consists of two digits. They are 0 & 1. Digit 1 is known as + * set bit in computer terms. + * @author [Kunal Nayak](https://github.com/Kunal766) + */ + +#include /// for assert +#include /// for IO operations + +/** + * @namespace bit_manipulation + * @brief Bit manipulation algorithms + */ +namespace bit_manipulation { + +/** + * @brief The main function implements checking the next number + * @param x the number that will be calculated + * @returns a number + */ +uint64_t next_higher_number(uint64_t x) { + uint64_t rightOne = 0; + uint64_t nextHigherOneBit = 0; + uint64_t rightOnesPattern = 0; + + uint64_t next = 0; + + if (x) { + // right most set bit + rightOne = x & -static_cast(x); + + // reset the pattern and set next higher bit + // left part of x will be here + nextHigherOneBit = x + rightOne; + + // nextHigherOneBit is now part [D] of the above explanation. + + // isolate the pattern + rightOnesPattern = x ^ nextHigherOneBit; + + // right adjust pattern + rightOnesPattern = (rightOnesPattern) / rightOne; + + // correction factor + rightOnesPattern >>= 2; + + // rightOnesPattern is now part [A] of the above explanation. + + // integrate new pattern (Add [D] and [A]) + next = nextHigherOneBit | rightOnesPattern; + } + + return next; +} + +} // namespace bit_manipulation + +/** + * @brief Self-test implementations + * @returns void + */ +static void test() { + // x = 4 return 8 + assert(bit_manipulation::next_higher_number(4) == 8); + // x = 6 return 9 + assert(bit_manipulation::next_higher_number(6) == 9); + // x = 13 return 14 + assert(bit_manipulation::next_higher_number(13) == 14); + // x = 64 return 128 + assert(bit_manipulation::next_higher_number(64) == 128); + // x = 15 return 23 + assert(bit_manipulation::next_higher_number(15) == 23); + // x= 32 return 64 + assert(bit_manipulation::next_higher_number(32) == 64); + // x = 97 return 98 + assert(bit_manipulation::next_higher_number(97) == 98); + // x = 1024 return 2048 + assert(bit_manipulation::next_higher_number(1024) == 2048); + + std::cout << "All test cases have successfully passed!" << std::endl; +} +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self-test implementations + return 0; +} diff --git a/cpu_scheduling_algorithms/CMakeLists.txt b/cpu_scheduling_algorithms/CMakeLists.txt new file mode 100644 index 00000000000..ce93cef26e3 --- /dev/null +++ b/cpu_scheduling_algorithms/CMakeLists.txt @@ -0,0 +1,16 @@ +# If necessary, use the RELATIVE flag, otherwise each source file may be listed +# with full pathname. The RELATIVE flag makes it easier to extract an executable's name +# automatically. + +file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp ) +foreach( testsourcefile ${APP_SOURCES} ) + string( REPLACE ".cpp" "" testname ${testsourcefile} ) # File type. Example: `.cpp` + add_executable( ${testname} ${testsourcefile} ) + + set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX) + if(OpenMP_CXX_FOUND) + target_link_libraries(${testname} OpenMP::OpenMP_CXX) + endif() + install(TARGETS ${testname} DESTINATION "bin/cpu_scheduling_algorithms") # Folder name. Do NOT include `<>` + +endforeach( testsourcefile ${APP_SOURCES} ) diff --git a/data_structures/treap.cpp b/data_structures/treap.cpp new file mode 100644 index 00000000000..522876941b1 --- /dev/null +++ b/data_structures/treap.cpp @@ -0,0 +1,259 @@ +/** + * @file + * @brief A balanced binary search tree (BST) on the basis of binary search tree + * and heap: the [Treap](https://en.wikipedia.org/wiki/Treap) algorithm + * implementation + * + * @details + * Implementation of the treap data structre + * + * Support operations including insert, erase, and query (the rank of specified + * element or the element ranked x) as the same as BST + * + * But these operations take O(log N) time, since treap keeps property of heap + * using rotate operation, and the desired depth of the tree is O(log N). + * There's very little chance that it will degenerate into a chain like BST + * + * @author [Kairao ZHENG](https://github.com/fgmn) + */ + +#include /// For array +#include /// For assert +#include /// For IO operations + +/** + * @namespace + * @brief Data Structures + */ +namespace data_structures { +/** + * @namespace + * @brief Functions for the [Treap](https://en.wikipedia.org/wiki/Treap) + * algorithm implementation + */ +namespace treap { +const int maxNode = 1e5 + 5; ///< maximum number of nodes +/** + * @brief Struct representation of the treap + */ +struct Treap { + int root = 0; ///< root of the treap + int treapCnt = 0; ///< Total number of current nodes in the treap + std::array key = {}; ///< Node identifier + std::array priority = {}; ///< Random priority + std::array, maxNode> childs = { + {}}; ///< [i][0] represents the + ///< left child of node i, and + ///[i][1] represents the right + std::array cnt = + {}; ///< Maintains the subtree size for ranking query + std::array size = {}; ///< The number of copies per node + /** + * @brief Initialization + */ + Treap() : treapCnt(1) { + priority[0] = INT32_MAX; + size[0] = 0; + } + /** + * @brief Update the subtree size of the node + * @param x The node to update + */ + void update(int x) { + size[x] = size[childs[x][0]] + cnt[x] + size[childs[x][1]]; + } + /** + * @brief Rotate without breaking the property of BST + * @param x The node to rotate + * @param t 0 represent left hand, while 1 right hand + */ + void rotate(int &x, int t) { + int y = childs[x][t]; + childs[x][t] = childs[y][1 - t]; + childs[y][1 - t] = x; + // The rotation will only change itself and its son nodes + update(x); + update(y); + x = y; + } + /** + * @brief Insert a value into the specified subtree (internal method) + * @param x Insert into the subtree of node x (Usually x=root) + * @param k Key to insert + */ + void _insert(int &x, int k) { + if (x) { + if (key[x] == k) { + cnt[x]++; + } // If the node already exists, the number of copies is ++ + else { + int t = (key[x] < k); // Insert according to BST properties + _insert(childs[x][t], k); + // After insertion, the heap properties are retained by rotation + if (priority[childs[x][t]] < priority[x]) { + rotate(x, t); + } + } + } else { // Create a new node + x = treapCnt++; + key[x] = k; + cnt[x] = 1; + priority[x] = rand(); // Random priority + childs[x][0] = childs[x][1] = 0; + } + update(x); + } + /** + * @brief Erase a value from the specified subtree (internal method) + * @param x Erase from the subtree of node x (Usually x=root) + * @param k Key to erase + */ + void _erase(int &x, int k) { + if (key[x] == k) { + if (cnt[x] > 1) { + cnt[x]--; + } // If the node has more than one copy, the number of copies -- + else { + if (childs[x][0] == 0 && childs[x][1] == 0) { + x = 0; + return; + } // If there are no children, delete and return + // Otherwise, we need to rotate the sons and delete them + // recursively + int t = (priority[childs[x][0]] > priority[childs[x][1]]); + rotate(x, t); + _erase(x, k); + } + } else { // Find the target value based on BST properties + _erase(childs[x][key[x] < k], k); + } + update(x); + } + /** + * @brief Find the KTH largest value (internal method) + * @param x Query the subtree of node x (Usually x=root) + * @param k The queried rank + * @return The element ranked number k + */ + int _get_k_th(int &x, int k) { + if (k <= size[childs[x][0]]) { + return _get_k_th(childs[x][0], k); + } + k -= size[childs[x][0]] + cnt[x]; + if (k <= 0) { + return key[x]; + } + return _get_k_th(childs[x][1], k); + } + /** + * @brief Query the rank of specified element (internal method) + * @param x Query the subtree of node x (Usually x=root) + * @param k The queried element + * @return The rank of element k + */ + int _get_rank(int x, int k) { + if (!x) { + return 0; + } + if (k == key[x]) { + return size[childs[x][0]] + 1; + } + else if (k < key[x]) { + return _get_rank(childs[x][0], k); + } + else { + return size[childs[x][0]] + cnt[x] + _get_rank(childs[x][1], k); + } + } + /** + * @brief Get the predecessor node of element k + * @param k The queried element + * @return The predecessor + */ + int get_predecessor(int k) { + int x = root, pre = -1; + while (x) { + if (key[x] < k) { + pre = key[x], x = childs[x][1]; + } else { + x = childs[x][0]; + } + } + return pre; + } + /** + * @brief Get the successor node of element k + * @param k The queried element + * @return The successor + */ + int get_next(int k) { + int x = root, next = -1; + while (x) { + if (key[x] > k) { + next = key[x], x = childs[x][0]; + } else { + x = childs[x][1]; + } + } + return next; + } + /** + * @brief Insert element (External method) + * @param k Key to insert + */ + void insert(int k) { _insert(root, k); } + /** + * @brief Erase element (External method) + * @param k Key to erase + */ + void erase(int k) { _erase(root, k); } + /** + * @brief Get the KTH largest value (External method) + * @param k The queried rank + * @return The element ranked number x + */ + int get_k_th(int k) { return _get_k_th(root, k); } + /** + * @brief Get the rank of specified element (External method) + * @param k The queried element + * @return The rank of element k + */ + int get_rank(int k) { return _get_rank(root, k); } +}; +} // namespace treap +} // namespace data_structures + +/** + * @brief Self-test implementations + * @returns void + */ +static void test() { + data_structures::treap::Treap mTreap; ///< Treap object instance + + mTreap.insert(1); + mTreap.insert(2); + mTreap.insert(3); + assert(mTreap.get_k_th(2) == 2); + mTreap.insert(4); + mTreap.insert(5); + mTreap.insert(6); + assert(mTreap.get_next(4) == 5); + mTreap.insert(7); + assert(mTreap.get_predecessor(7) == 6); + mTreap.erase(4); + assert(mTreap.get_k_th(4) == 5); + assert(mTreap.get_rank(5) == 4); + mTreap.insert(10); + assert(mTreap.get_rank(10) == 7); + assert(mTreap.get_predecessor(10) == 7); + + std::cout << "All tests have successfully passed!\n"; +} +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self-test implementations + return 0; +} diff --git a/dynamic_programming/armstrong_number.cpp b/dynamic_programming/armstrong_number.cpp index 5f9a62f45dd..a8d3e81d747 100644 --- a/dynamic_programming/armstrong_number.cpp +++ b/dynamic_programming/armstrong_number.cpp @@ -1,39 +1,93 @@ -// Program to check whether a number is an armstrong number or not -#include -#include -using std::cin; -using std::cout; +/** + * @file + * @brief Checks whether a number is an [Armstrong + * Number](https://en.wikipedia.org/wiki/Narcissistic_number) or not. + * + * @details + * An Armstrong number is a number that is the sum of its own digits each raised + * to the power of the number of digits. For example: 153 is an Armstrong number + * since 153 = 1^3 + 5^3 + 3^3. + * + * A few examples of valid armstrong numbers: + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407, 1634, 8208, 9474, 54748, + * 92727, 93084. + * + * Armstrong numbers are also known as Narcissistic Numbers, as stated in + * Wikipedia. + * + * @author [Shivam Singhal](https://github.com/shivhek25) + * @author [David Leal](https://github.com/Panquesito7) + */ -int main() { - int n = 0, temp = 0, rem = 0, count = 0, sum = 0; - cout << "Enter a number: "; - cin >> n; +#include /// for assert +#include /// for std::pow +#include /// for IO operations + +/** + * @namespace + * @brief Dynamic Programming algorithms + */ +namespace dynamic_programming { - temp = n; +/** + * @brief Checks if the given number is armstrong or not. + * @param number the number to check + * @returns false if the given number is NOT armstrong + * @returns true if the given number IS armstrong + */ +template +bool is_armstrong(const T &number) { + int count = 0, temp = number, result = 0, rem = 0; - /* First Count the number of digits - in the given number */ + // Count the number of digits of the given number. + // For example: 153 would be 3 digits. while (temp != 0) { temp /= 10; count++; } - /* Calculation for checking of armstrongs number i.e. - in an n-digit number sum of the digits is raised to a power of n - is equal to the original number */ - - temp = n; + // Calculation for checking of armstrongs number i.e. + // in an n-digit number sum of the digits is raised to a power of `n` is + // equal to the original number. + temp = number; while (temp != 0) { rem = temp % 10; - sum += static_cast(pow(rem, count)); + result += static_cast(std::pow(rem, count)); temp /= 10; } - if (sum == n) { - cout << n << " is an armstrong number"; + if (result == number) { + return true; } else { - cout << n << " is not an armstrong number"; + return false; } +} +} // namespace dynamic_programming +/** + * @brief Self-test implementations + * @returns void + */ +static void tests() { + assert(dynamic_programming::is_armstrong(153) == true); + assert(dynamic_programming::is_armstrong(1) == true); + assert(dynamic_programming::is_armstrong(0) == true); + assert(dynamic_programming::is_armstrong(370) == true); + assert(dynamic_programming::is_armstrong(1634) == true); + assert(dynamic_programming::is_armstrong(580) == false); + assert(dynamic_programming::is_armstrong(15) == false); + assert(dynamic_programming::is_armstrong(1024) == false); + assert(dynamic_programming::is_armstrong(989) == false); + assert(dynamic_programming::is_armstrong(103) == false); + + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + tests(); // run self-test implementations return 0; } diff --git a/dynamic_programming/longest_common_string.cpp b/dynamic_programming/longest_common_string.cpp index 81fa8a0026a..47e423d92ff 100644 --- a/dynamic_programming/longest_common_string.cpp +++ b/dynamic_programming/longest_common_string.cpp @@ -1,53 +1,159 @@ -#include -using namespace std; +/** + * @file + * @brief contains the definition of the function ::longest_common_string_length + * @details + * the function ::longest_common_string_length computes the length + * of the longest common string which can be created of two input strings + * by removing characters from them + * + * @author [Nikhil Arora](https://github.com/nikhilarora068) + * @author [Piotr Idzik](https://github.com/vil02) + */ -int max(int a, int b) { return (a > b) ? a : b; } +#include /// for assert +#include /// for std::cout +#include /// for std::string +#include /// for std::move +#include /// for std::vector -int main() { - char str1[] = "DEFBCD"; - char str2[] = "ABDEFJ"; - int i, j, k; - int n = strlen(str1) + 1; - int m = strlen(str2) + 1; - // cout<> sub_sols( + size_a + 1, std::vector(size_b + 1, 0)); - /*for(i=0;i ma) { - ma = a[i][j]; - indi = i; - indj = j; + const auto limit = static_cast(-1); + for (std::size_t pos_a = size_a - 1; pos_a != limit; --pos_a) { + for (std::size_t pos_b = size_b - 1; pos_b != limit; --pos_b) { + if (string_a[pos_a] == string_b[pos_b]) { + sub_sols[pos_a][pos_b] = 1 + sub_sols[pos_a + 1][pos_b + 1]; + } else { + sub_sols[pos_a][pos_b] = std::max(sub_sols[pos_a + 1][pos_b], + sub_sols[pos_a][pos_b + 1]); } } } - cout << str1 << "\n"; - cout << str2 << "\n"; + return sub_sols[0][0]; +} + +/** + * @brief represents single example inputs and expected output of the function + * ::longest_common_string_length + */ +struct TestCase { + const std::string string_a; + const std::string string_b; + const std::size_t common_string_len; + + TestCase(std::string string_a, std::string string_b, + const std::size_t in_common_string_len) + : string_a(std::move(string_a)), + string_b(std::move(string_b)), + common_string_len(in_common_string_len) {} +}; - cout << "longest string size = " << ma /*<<" "< get_test_cases() { + return {TestCase("", "", 0), + TestCase("ab", "ab", 2), + TestCase("ab", "ba", 1), + TestCase("", "xyz", 0), + TestCase("abcde", "ace", 3), + TestCase("BADANA", "ANADA", 3), + TestCase("BADANA", "CANADAS", 3), + TestCase("a1a234a5aaaa6", "A1AAAA234AAA56AAAAA", 6), + TestCase("123x", "123", 3), + TestCase("12x3x", "123", 3), + TestCase("1x2x3x", "123", 3), + TestCase("x1x2x3x", "123", 3), + TestCase("x12x3x", "123", 3)}; +} + +/** + * @brief checks the function ::longest_common_string_length agains example data + * @param test_cases list of test cases + * @tparam type representing a list of test cases + */ +template +static void test_longest_common_string_length(const TestCases& test_cases) { + for (const auto& cur_tc : test_cases) { + assert(longest_common_string_length(cur_tc.string_a, cur_tc.string_b) == + cur_tc.common_string_len); + } +} + +/** + * @brief checks if the function ::longest_common_string_length returns the same + * result when its argument are flipped + * @param test_cases list of test cases + * @tparam type representing a list of test cases + */ +template +static void test_longest_common_string_length_is_symmetric( + const TestCases& test_cases) { + for (const auto& cur_tc : test_cases) { + assert(longest_common_string_length(cur_tc.string_b, cur_tc.string_a) == + cur_tc.common_string_len); + } +} + +/** + * @brief reverses a given string + * @param in_str input string + * @return the string in which the characters appear in the reversed order as in + * in_str + */ +std::string reverse_str(const std::string& in_str) { + return {in_str.rbegin(), in_str.rend()}; +} + +/** + * @brief checks if the function ::longest_common_string_length returns the same + * result when its inputs are reversed + * @param test_cases list of test cases + * @tparam type representing a list of test cases + */ +template +static void test_longest_common_string_length_for_reversed_inputs( + const TestCases& test_cases) { + for (const auto& cur_tc : test_cases) { + assert(longest_common_string_length(reverse_str(cur_tc.string_a), + reverse_str(cur_tc.string_b)) == + cur_tc.common_string_len); + } +} + +/** + * @brief runs all tests for ::longest_common_string_length funcion + */ +static void tests() { + const auto test_cases = get_test_cases(); + assert(test_cases.size() > 0); + test_longest_common_string_length(test_cases); + test_longest_common_string_length_is_symmetric(test_cases); + test_longest_common_string_length_for_reversed_inputs(test_cases); + + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + tests(); + return 0; } diff --git a/games/CMakeLists.txt b/games/CMakeLists.txt new file mode 100644 index 00000000000..734509aa056 --- /dev/null +++ b/games/CMakeLists.txt @@ -0,0 +1,16 @@ +# If necessary, use the RELATIVE flag, otherwise each source file may be listed +# with full pathname. The RELATIVE flag makes it easier to extract an executable's name +# automatically. + +file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp ) +foreach( testsourcefile ${APP_SOURCES} ) + string( REPLACE ".cpp" "" testname ${testsourcefile} ) # File type. Example: `.cpp` + add_executable( ${testname} ${testsourcefile} ) + + set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX) + if(OpenMP_CXX_FOUND) + target_link_libraries(${testname} OpenMP::OpenMP_CXX) + endif() + install(TARGETS ${testname} DESTINATION "bin/games") # Folder name. Do NOT include `<>` + +endforeach( testsourcefile ${APP_SOURCES} ) diff --git a/games/memory_game.cpp b/games/memory_game.cpp new file mode 100644 index 00000000000..9283340ae89 --- /dev/null +++ b/games/memory_game.cpp @@ -0,0 +1,416 @@ +/** + * @file + * @brief A simple [Memory Game](https://en.wikipedia.org/wiki/Matching_game) + * with **3 different sizes** and multiple letters. + * @details + * The game consists on finding **the pair** of all the given letters depending + * on the table size. Once all of the instances are all found, the game will end + * and will ask you if you'd like to play again or not. + * + * It provides **3 different sizes** available that the user can choose (4x2, + * 5x2, 7x2). 7x2 being the biggest table size and hardest mode. The bigger the + * size, **the more letters are available**. + * + * @author [David Leal](https://github.com/Panquesito7) + */ + +#include /// for std::shuffle() +#include /// for std::srand() +#include /// for std::time() +#include /// for IO operations +#include /// for std::mt19937 +#include /// for std::vector + +// `Sleep` is only available in Windows in milliseconds. +// However, on Unix/Linux systems it is `sleep`, in seconds. +#ifdef _WIN32 +#include /// for Sleep() +template +constexpr typename std::enable_if::value, void>::type SLEEP( + T milliseconds) { + Sleep(milliseconds * 1000); +} +#else +#include /// for sleep() +template +constexpr T SLEEP(T seconds) { + return sleep(seconds); +} +#endif + +/** + * @namespace + * @brief (Mini)game implementations. + */ +namespace games { +/** + * @namespace + * @brief Functions for the [Memory + * Game](https://en.wikipedia.org/wiki/Matching_game) implementation + */ +namespace memory_game { +/** + * @brief Utility function to verify if the given input is a number or not. + * This is very useful to prevent the program being stuck in a loop. + * @tparam T The type of the input + * @param input The input to check. + * @returns false if the input IS empty or if it contains a non-digit character + * @returns true if the input is NOT empty and if it contains only digit + * characters + */ +template +bool is_number(const T &input) { + if (std::cin.fail()) { + std::cin.clear(); + std::cin.ignore(256, '\n'); + + return false; + } + + return true; +} + +/** + * @brief Initializes the table with the letters. + * @tparam T The type of the table. + * @param table The table to initialize. + * @returns void + */ +template +void init(std::vector *table) { + std::vector letters(7); + + // Decrease / increase the number of letters depending on the size. + if ((*table).size() == 10) { // 5x2 + letters = {'A', 'E', 'Z', 'P', 'D'}; + } else if ((*table).size() == 8) { // 4x2 + letters = {'A', 'E', 'Z', 'D'}; + } else if ((*table).size() == 14) { // 7x2 + letters = {'A', 'E', 'Z', 'P', 'D', 'B', 'M'}; + } + + std::vector pairs; + for (char letter : letters) { + pairs.push_back(letter); + pairs.push_back(letter); + } + + std::shuffle(pairs.begin(), pairs.end(), + std::mt19937(std::random_device()())); + + for (int i = 0; i < (*table).size(); i++) { + (*table)[i] = pairs[i]; + } + + std::cout << "All available types are: "; + + for (int i = 0; i < letters.size(); i++) { + if (i == letters.size() - 1) { + std::cout << "and " << letters[i] << ".\n\n"; + } else { + std::cout << letters[i] << ", "; + } + } +} + +/** + * @brief Utility function to print the table. + * @tparam T The type of the table. + * @param table The table to print. + * @returns void + */ +template +void print_table(const std::vector &table) { + std::cout << "| "; + std::vector table_print(table.size()); + + for (int i = 0; i < table.size(); i++) { + table_print[i] = ' '; + + if (table[i] != 0) { + table_print[i] = table[i]; + } + } + + for (int i = 0; i < table.size(); i++) { + if (i % 5 == 0 && i != 0) { + std::cout << "\n| "; + } + + std::cout << table_print[i] << " | "; + } +} + +// Prototype function. This is needed as `ask_data` calls `reset_data`, and +// `reset_data` calls `ask_data`. +template +void reset_data(const std::vector &, int *, int *, int *); + +/** + * @brief Function that asks the user for their + * input in the table they previously chose. + * @tparam T The type of the table. + * @param table The table that's used to get the user's input and data. + * @param answer The user's answer. + * @param old_answer The user's previous answer. + * @param memory_count A counter to check if the user has already answered two + * values. + * @returns void + */ +template +void ask_data(const std::vector &table, int *answer, int *old_answer, + int *memory_count) { + (*old_answer) = (*answer); + print_table(table); + + std::cout << "\n\nType your response here (number index):\n"; + std::cin >> (*answer); + + if (!is_number((*answer))) { + std::cout << "\nYou must enter a valid number.\n\n"; + reset_data(table, answer, old_answer, memory_count); + } + + // Increase the memory count, which will be later on used for checking if + // the user has already answered two values. + (*memory_count)++; + + if (((*answer) > table.size()) || ((*answer) < 1)) { + std::cout << "\nYou can't check a value that doesn't exist (or an " + "invalid number).\n\n"; + reset_data(table, answer, old_answer, memory_count); + } + + if ((*old_answer) == (*answer)) { + std::cout << "\nYou can't check the same value twice.\n\n"; + reset_data(table, answer, old_answer, memory_count); + } + + // If two matches are answered already, but the user checkes a non-answered + // and an answered value, the program will mark it as no match, however, we + // must not allow the user to check the same value twice. + if ((table[(*answer) - 1] != 0) && + ((table[(*old_answer)] == 0) || (table[(*old_answer)] != 0))) { + std::cout << "\nYou can't check the same value twice.\n\n"; + reset_data(table, answer, old_answer, memory_count); + } +} + +/** + * @brief Utility function that resets the data if the user enters an invalid + * value. + * @tparam T The type of the table. + * @param table The table that will be used to call `ask_data()`. + * @param answer The user's answer. + * @param old_answer The user's previous answer. + * @param memory_count A counter to check if the user has already answered two + * values. + * @returns void + */ +template +void reset_data(const std::vector &table, int *answer, int *old_answer, + int *memory_count) { + (*answer) = (*old_answer); + (*memory_count)--; + + ask_data(table, answer, old_answer, memory_count); +} + +/** + * @brief Checks if the two values given by the user match. + * @tparam T The type of the table. + * @param table_empty The table with no values, slowly assigned from `table` + * depending on the user's input. + * @param table The table with the original values. + * @param answer The user's answer. + * @param first_time A boolean to check if the user has already answered a + * value. + * @param old_answer The user's previous answer. + * @param memory_count A counter to check if the user has already answered two + * values. + * @returns true IF the values given by the user match + * @returns false if the values given by the user do NOT match + */ +template +bool match(const std::vector &table, std::vector *table_empty, + const int &answer, bool *first_time, int *old_answer, + int *memory_count) { + if ((*first_time) == true) { + return true; + } + + // Search across the whole table and if the two values match, keep results, + // otherwise, hide 'em up. + for (int i = 0; i < table.size() + 1; i++) { + if (i == answer) { + if (table[i - 1] == table[(*old_answer) - 1]) { + (*first_time) = true; + (*memory_count) = 0; + + (*old_answer) = 0; + return true; + } else { + std::cout << "\nNo match (value was " << table[i - 1] + << ", index is " << i << ").\n\n"; + + (*table_empty)[(*old_answer) - 1] = 0; + (*table_empty)[answer - 1] = 0; + + (*first_time) = true; + (*memory_count) = 0; + + (*old_answer) = 0; + return false; + } + } + } + + return false; +} + +/** + * @brief Function to assign the results to the table. + * + * Also checkes if the user has answered all the values already, as well as + * verify if the user made a match or not. + * @tparam T The type of the tables. + * @param table_empty The table with no values, slowly assigned from `table` + * depending on the user's input. + * @param table The table with the original values. + * @param answer The user's answer. + * @param first_time A boolean to check if the user has already answered a + * value. + * @param old_answer The user's previous answer. + * @param memory_count A counter to check if the user has already answered two + * values. + * @returns void + */ +template +void assign_results(std::vector *table_empty, std::vector *table, + int *answer, bool *first_time, int *old_answer, + int *memory_count) { + // Search through the entire table and if the answer matches the index, show + // the value. If it doesn't match, hide both the values. Don't forget to + // keep older values already answered. + for (int i = 0; i < (*table).size() + 1; i++) { + if (i == (*answer)) { + if (match((*table), table_empty, (*answer), first_time, old_answer, + memory_count) == true) { + (*table_empty)[i - 1] = (*table)[i - 1]; + (*first_time) = true; + } + } + } + + if ((*memory_count) == 1) { + (*first_time) = false; + (*memory_count) = 0; + } + + char try_again = 'n'; + + // Has the user finished the game? Use a `for` loop, and if the table is + // full, ask the user if he wants to play again. + for (int i = 0; i < (*table).size() + 1; i++) { + if ((*table_empty)[i] == 0) { + break; + } else if (i == (*table).size() - 1) { + print_table((*table)); + + std::cout << "\n\nYou won. Congratulations! Do you want to play " + "again? (y/n)\n"; + std::cout + << "Size " << (*table).size() + << " will be used. This can be changed by re-running the game."; + std::cin >> try_again; + if (try_again == 'y') { + // This is needed when checking if the user has two matches + // already. + for (int i = 0; i < (*table_empty).size(); i++) { + (*table_empty)[i] = 0; + } + + init(table); + } else if (try_again == 'n') { + std::cout << "\nThanks for playing the game!\n"; + SLEEP(3); + + exit(0); + } else { + std::cout << "\nInvalid input (exitting...).\n"; + SLEEP(3); + + exit(0); + } + } + } + + // Ask data again. + ask_data((*table_empty), answer, old_answer, memory_count); + assign_results(table_empty, table, answer, first_time, old_answer, + memory_count); +} +} // namespace memory_game +} // namespace games + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + // Start randomizer. This changes the values every time. + std::srand(std::time(nullptr)); + + int size = 0; ///< Size of the table. + int selection = 0; ///< Selection of the size (4x2, 5x2, 7x2). + + int response = 0; ///< The answer (number index) that the user chose. + int old_answer = 0; ///< Previous answer (number index). + + int memory_count = + 0; ///< Counter to check if the user has already answered two values. + bool first_time = true; ///< Whether the user has answered 1 value or not + ///< (previous answered values do not count). + + std::cout << "\tMEMORY GAME\n"; + std::cout << std::boolalpha; + std::cout << std::is_literal_type::value; + + do { + std::cout << "\n1. 4x2 (1)"; + std::cout << "\n2. 5x2 (2)"; + std::cout << "\n3. 7x2 (3)\n"; + + std::cout << "\nChoose table size: "; + std::cin >> selection; + } while ((selection < 1 || selection > 3) && + (!games::memory_game::is_number(selection))); + + switch (selection) { + case 1: + size = 8; + break; + case 2: + size = 10; + break; + case 3: + size = 14; + break; + default: + size = 10; + break; + } + + std::vector table(size); + std::vector table_empty(size); + + std::cout << "\n"; + + games::memory_game::init(&table); + games::memory_game::ask_data(table_empty, &response, &old_answer, + &memory_count); + games::memory_game::assign_results(&table_empty, &table, &response, + &first_time, &old_answer, &memory_count); + + return 0; +} diff --git a/graphics/CMakeLists.txt b/graphics/CMakeLists.txt index 757142f2468..23ffcfae179 100644 --- a/graphics/CMakeLists.txt +++ b/graphics/CMakeLists.txt @@ -6,8 +6,8 @@ if(OpenGL_FOUND) include(ExternalProject) ExternalProject_Add ( FREEGLUT-PRJ - URL https://github.com/FreeGLUTProject/freeglut/releases/download/v3.2.2/freeglut-3.2.2.tar.gz - URL_MD5 485c1976165315fc42c0b0a1802816d9 + URL https://github.com/FreeGLUTProject/freeglut/releases/download/v3.4.0/freeglut-3.4.0.tar.gz + URL_MD5 f1621464e6525d0368976870cab8f418 CMAKE_GENERATOR ${CMAKE_GENERATOR} CMAKE_GENERATOR_TOOLSET ${CMAKE_GENERATOR_TOOLSET} CMAKE_GENERATOR_PLATFORM ${CMAKE_GENERATOR_PLATFORM} diff --git a/greedy_algorithms/dijkstra.cpp b/greedy_algorithms/dijkstra.cpp index e4450379c5d..739182daf12 100644 --- a/greedy_algorithms/dijkstra.cpp +++ b/greedy_algorithms/dijkstra.cpp @@ -1,23 +1,54 @@ -#include -#include - -using namespace std; - -// Wrapper class for storing a graph +/** + * @file + * @brief [Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) algorithm + * implementation + * @details + * _Quote from Wikipedia._ + * + * **Dijkstra's algorithm** is an algorithm for finding the + * shortest paths between nodes in a weighted graph, which may represent, for + * example, road networks. It was conceived by computer scientist Edsger W. + * Dijkstra in 1956 and published three years later. + * + * @author [David Leal](https://github.com/Panquesito7) + * @author [Arpan Jain](https://github.com/arpanjain97) + */ + +#include /// for assert +#include /// for INT_MAX +#include /// for IO operations +#include /// for std::vector + +/** + * @namespace + * @brief Greedy Algorithms + */ +namespace greedy_algorithms { +/** + * @namespace + * @brief Functions for the [Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) algorithm implementation + */ +namespace dijkstra { +/** + * @brief Wrapper class for storing a graph + */ class Graph { public: - int vertexNum; - int **edges; - - // Constructs a graph with V vertices and E edges - Graph(const int V) { - // initializes the array edges. - this->edges = new int *[V]; + int vertexNum = 0; + std::vector> edges{}; + + /** + * @brief Constructs a graph + * @param V number of vertices of the graph + */ + explicit Graph(const int V) { + // Initialize the array edges + this->edges = std::vector>(V, std::vector(V, 0)); for (int i = 0; i < V; i++) { - edges[i] = new int[V]; + edges[i] = std::vector(V, 0); } - // fills the array with zeros. + // Fills the array with zeros for (int i = 0; i < V; i++) { for (int j = 0; j < V; j++) { edges[i][j] = 0; @@ -27,13 +58,28 @@ class Graph { this->vertexNum = V; } - // Adds the given edge to the graph - void addEdge(int src, int dst, int weight) { + /** + * @brief Adds an edge to the graph + * @param src the graph the edge should be added to + * @param dst the position where the edge should be added to + * @param weight the weight of the edge that should be added + * @returns void + */ + void add_edge(int src, int dst, int weight) { this->edges[src][dst] = weight; } }; -// Utility function to find minimum distance vertex in mdist -int minDistance(int mdist[], bool vset[], int V) { + +/** + * @brief Utility function that finds + * the vertex with the minimum distance in `mdist`. + * + * @param mdist array of distances to each vertex + * @param vset array indicating inclusion in the shortest path tree + * @param V the number of vertices in the graph + * @returns index of the vertex with the minimum distance + */ +int minimum_distance(std::vector mdist, std::vector vset, int V) { int minVal = INT_MAX, minInd = 0; for (int i = 0; i < V; i++) { if (!vset[i] && (mdist[i] < minVal)) { @@ -45,27 +91,42 @@ int minDistance(int mdist[], bool vset[], int V) { return minInd; } -// Utility function to print distances -void print(int dist[], int V) { - cout << "\nVertex Distance" << endl; +/** + * @brief Utility function to print the distances to vertices. + * + * This function prints the distances to each vertex in a tabular format. If the + * distance is equal to INT_MAX, it is displayed as "INF". + * + * @param dist An array representing the distances to each vertex. + * @param V The number of vertices in the graph. + * @return void + */ +void print(std::vector dist, int V) { + std::cout << "\nVertex Distance\n"; for (int i = 0; i < V; i++) { - if (dist[i] < INT_MAX) - cout << i << "\t" << dist[i] << endl; - else - cout << i << "\tINF" << endl; + if (dist[i] < INT_MAX) { + std::cout << i << "\t" << dist[i] << "\n"; + } + else { + std::cout << i << "\tINF" << "\n"; + } } } -// The main function that finds the shortest path from given source -// to all other vertices using Dijkstra's Algorithm.It doesn't work on negative -// weights -void Dijkstra(Graph graph, int src) { +/** + * @brief The main function that finds the shortest path from a given source + * to all other vertices using Dijkstra's Algorithm. + * @note This doesn't work on negative weights. + * @param graph the graph to be processed + * @param src the source of the given vertex + * @returns void + */ +void dijkstra(Graph graph, int src) { int V = graph.vertexNum; - int mdist[V]; // Stores updated distances to vertex - bool vset[V]; // vset[i] is true if the vertex i included - // in the shortest path tree + std::vector mdist{}; // Stores updated distances to the vertex + std::vector vset{}; // `vset[i]` is true if the vertex `i` is included in the shortest path tree - // Initialise mdist and vset. Set distance of source as zero + // Initialize `mdist and `vset`. Set the distance of the source as zero for (int i = 0; i < V; i++) { mdist[i] = INT_MAX; vset[i] = false; @@ -73,9 +134,9 @@ void Dijkstra(Graph graph, int src) { mdist[src] = 0; - // iterate to find shortest path + // iterate to find the shortest path for (int count = 0; count < V - 1; count++) { - int u = minDistance(mdist, vset, V); + int u = minimum_distance(mdist, vset, V); vset[u] = true; @@ -89,36 +150,53 @@ void Dijkstra(Graph graph, int src) { print(mdist, V); } +} // namespace dijkstra +} // namespace greedy_algorithms -// Driver Function -int main() { - int V, E, gsrc; - int src, dst, weight; - cout << "Enter number of vertices: "; - cin >> V; - cout << "Enter number of edges: "; - cin >> E; - Graph G(V); - for (int i = 0; i < E; i++) { - cout << "\nEdge " << i + 1 << "\nEnter source: "; - cin >> src; - cout << "Enter destination: "; - cin >> dst; - cout << "Enter weight: "; - cin >> weight; - - // makes sure source and destionation are in the proper bounds. - if (src >= 0 && src < V && dst >= 0 && dst < V) { - G.addEdge(src, dst, weight); - } else { - cout << "source and/or destination out of bounds" << endl; - i--; - continue; - } - } - cout << "\nEnter source:"; - cin >> gsrc; - Dijkstra(G, gsrc); +/** + * @brief Self-test implementations + * @returns void + */ +static void tests() { + greedy_algorithms::dijkstra::Graph graph(8); + + // 1st test. + graph.add_edge(6, 2, 4); + graph.add_edge(2, 6, 4); + assert(graph.edges[6][2] == 4); + + // 2nd test. + graph.add_edge(0, 1, 1); + graph.add_edge(1, 0, 1); + + assert(graph.edges[0][1] == 1); + + // 3rd test. + graph.add_edge(0, 2, 7); + graph.add_edge(2, 0, 7); + graph.add_edge(1, 2, 1); + graph.add_edge(2, 1, 1); + + assert(graph.edges[0][2] == 7); + + // 4th test. + graph.add_edge(1, 3, 3); + graph.add_edge(3, 1, 3); + graph.add_edge(1, 4, 2); + graph.add_edge(4, 1, 2); + graph.add_edge(2, 3, 2); + + assert(graph.edges[1][3] == 3); + + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + tests(); // run self-test implementations return 0; } diff --git a/greedy_algorithms/kruskals_minimum_spanning_tree.cpp b/greedy_algorithms/kruskals_minimum_spanning_tree.cpp index ed7f82b7215..7925eb0373e 100644 --- a/greedy_algorithms/kruskals_minimum_spanning_tree.cpp +++ b/greedy_algorithms/kruskals_minimum_spanning_tree.cpp @@ -1,9 +1,42 @@ -#include -#include +/** + * @file + * @brief [Kruskals Minimum Spanning + * Tree](https://www.simplilearn.com/tutorials/data-structure-tutorial/kruskal-algorithm) + * implementation + * + * @details + * _Quoted from + * [Simplilearn](https://www.simplilearn.com/tutorials/data-structure-tutorial/kruskal-algorithm)._ + * + * Kruskal’s algorithm is the concept that is introduced in the graph theory of + * discrete mathematics. It is used to discover the shortest path between two + * points in a connected weighted graph. This algorithm converts a given graph + * into the forest, considering each node as a separate tree. These trees can + * only link to each other if the edge connecting them has a low value and + * doesn’t generate a cycle in MST structure. + * + * @author [coleman2246](https://github.com/coleman2246) + */ -void findMinimumEdge(int INFINITY, std::array, 6> graph) { +#include /// for array +#include /// for IO operations + +/** + * @namespace + * @brief Greedy Algorithms + */ +namespace greedy_algorithms { +/** + * @brief Finds the minimum edge of the given graph. + * @param infinity Defines the infinity of the graph + * @param graph The graph that will be used to find the edge + * @returns void + */ +template +void findMinimumEdge(const int &infinity, + const std::array, 6> &graph) { for (int i = 0; i < graph.size(); i++) { - int min = INFINITY; + int min = infinity; int minIndex = 0; for (int j = 0; j < graph.size(); j++) { if (graph[i][j] != 0 && graph[i][j] < min) { @@ -12,10 +45,15 @@ void findMinimumEdge(int INFINITY, std::array, 6> graph) { } } std::cout << i << " - " << minIndex << "\t" << graph[i][minIndex] - << std::endl; + << "\n"; } } +} // namespace greedy_algorithms +/** + * @brief Main function + * @returns 0 on exit + */ int main() { constexpr int INFINITY = 99999; std::array, 6> graph{ @@ -26,6 +64,6 @@ int main() { INFINITY, 3, 1, 5, 0, INFINITY, INFINITY, INFINITY, INFINITY, 7, INFINITY, 0}; - findMinimumEdge(INFINITY, graph); + greedy_algorithms::findMinimumEdge(INFINITY, graph); return 0; } diff --git a/hashing/sha256.cpp b/hashing/sha256.cpp new file mode 100644 index 00000000000..0eae0bd361e --- /dev/null +++ b/hashing/sha256.cpp @@ -0,0 +1,329 @@ +/** + * @file + * @author [Md. Anisul Haque](https://github.com/mdanisulh) + * @brief Simple C++ implementation of the [SHA-256 Hashing Algorithm] + * (https://en.wikipedia.org/wiki/SHA-2) + * + * @details + * [SHA-2](https://en.wikipedia.org/wiki/SHA-2) is a set of cryptographic hash + * functions that was designed by the + * [NSA](https://en.wikipedia.org/wiki/National_Security_Agency) and first + * published in 2001. SHA-256 is a part of the SHA-2 family. SHA-256 is widely + * used for authenticating software packages and secure password hashing. + */ + +#include /// For std::array +#include /// For assert +#include /// For uint8_t, uint32_t and uint64_t data types +#include /// For std::setfill and std::setw +#include /// For IO operations +#include /// For std::stringstream +#include /// For std::move +#include /// For std::vector + +/** + * @namespace hashing + * @brief Hashing algorithms + */ +namespace hashing { +/** + * @namespace SHA-256 + * @brief Functions for the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) + * algorithm implementation + */ +namespace sha256 { +/** + * @class Hash + * @brief Contains hash array and functions to update it and convert it to a + * hexadecimal string + */ +class Hash { + // Initialize array of hash values with first 32 bits of the fractional + // parts of the square roots of the first 8 primes 2..19 + std::array hash = {0x6A09E667, 0xBB67AE85, 0x3C6EF372, + 0xA54FF53A, 0x510E527F, 0x9B05688C, + 0x1F83D9AB, 0x5BE0CD19}; + + public: + void update(const std::array &blocks); + std::string to_string() const; +}; + +/** + * @brief Rotates the bits of a 32-bit unsigned integer + * @param n Integer to rotate + * @param rotate Number of bits to rotate + * @return uint32_t The rotated integer + */ +uint32_t right_rotate(uint32_t n, size_t rotate) { + return (n >> rotate) | (n << (32 - rotate)); +} + +/** + * @brief Updates the hash array + * @param blocks Message schedule array + * @return void + */ +void Hash::update(const std::array &blocks) { + // Initialize array of round constants with first 32 bits of the fractional + // parts of the cube roots of the first 64 primes 2..311 + const std::array round_constants = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, + 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, + 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, + 0x06CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, + 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, + 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2}; + + // Initialize working variables + auto a = hash[0]; + auto b = hash[1]; + auto c = hash[2]; + auto d = hash[3]; + auto e = hash[4]; + auto f = hash[5]; + auto g = hash[6]; + auto h = hash[7]; + + // Compression function main loop + for (size_t block_num = 0; block_num < 64; ++block_num) { + const auto s1 = + right_rotate(e, 6) ^ right_rotate(e, 11) ^ right_rotate(e, 25); + const auto ch = (e & f) ^ (~e & g); + const auto temp1 = + h + s1 + ch + round_constants[block_num] + blocks[block_num]; + const auto s0 = + right_rotate(a, 2) ^ right_rotate(a, 13) ^ right_rotate(a, 22); + const auto maj = (a & b) ^ (a & c) ^ (b & c); + const auto temp2 = s0 + maj; + + h = g; + g = f; + f = e; + e = d + temp1; + d = c; + c = b; + b = a; + a = temp1 + temp2; + } + + // Update hash values + hash[0] += a; + hash[1] += b; + hash[2] += c; + hash[3] += d; + hash[4] += e; + hash[5] += f; + hash[6] += g; + hash[7] += h; +} + +/** + * @brief Convert the hash to a hexadecimal string + * @return std::string Final hash value + */ +std::string Hash::to_string() const { + std::stringstream ss; + for (size_t i = 0; i < 8; ++i) { + ss << std::hex << std::setfill('0') << std::setw(8) << hash[i]; + } + return ss.str(); +} + +/** + * @brief Computes size of the padded input + * @param input Input string + * @return size_t Size of the padded input + */ +std::size_t compute_padded_size(const std::size_t input_size) { + if (input_size % 64 < 56) { + return input_size + 64 - (input_size % 64); + } + return input_size + 128 - (input_size % 64); +} + +/** + * @brief Returns the byte at position byte_num in in_value + * @param in_value Input value + * @param byte_num Position of byte to be returned + * @return uint8_t Byte at position byte_num + */ +template +uint8_t extract_byte(const T in_value, const std::size_t byte_num) { + if (sizeof(in_value) <= byte_num) { + throw std::out_of_range("Byte at index byte_num does not exist"); + } + return (in_value >> (byte_num * 8)) & 0xFF; +} + +/** + * @brief Returns the character at pos after the input is padded + * @param input Input string + * @param pos Position of character to be returned + * @return char Character at the index pos in the padded string + */ +char get_char(const std::string &input, std::size_t pos) { + const auto input_size = input.length(); + if (pos < input_size) { + return input[pos]; + } + if (pos == input_size) { + return '\x80'; + } + const auto padded_input_size = compute_padded_size(input_size); + if (pos < padded_input_size - 8) { + return '\x00'; + } + if (padded_input_size <= pos) { + throw std::out_of_range("pos is out of range"); + } + return static_cast( + extract_byte(input_size * 8, padded_input_size - pos - 1)); +} + +/** + * @brief Creates the message schedule array + * @param input Input string + * @param byte_num Position of the first byte of the chunk + * @return std::array Message schedule array + */ +std::array create_message_schedule_array(const std::string &input, + const size_t byte_num) { + std::array blocks{}; + + // Copy chunk into first 16 words of the message schedule array + for (size_t block_num = 0; block_num < 16; ++block_num) { + blocks[block_num] = + (static_cast(get_char(input, byte_num + block_num * 4)) + << 24) | + (static_cast(get_char(input, byte_num + block_num * 4 + 1)) + << 16) | + (static_cast(get_char(input, byte_num + block_num * 4 + 2)) + << 8) | + static_cast(get_char(input, byte_num + block_num * 4 + 3)); + } + + // Extend the first 16 words into remaining 48 words of the message schedule + // array + for (size_t block_num = 16; block_num < 64; ++block_num) { + const auto s0 = right_rotate(blocks[block_num - 15], 7) ^ + right_rotate(blocks[block_num - 15], 18) ^ + (blocks[block_num - 15] >> 3); + const auto s1 = right_rotate(blocks[block_num - 2], 17) ^ + right_rotate(blocks[block_num - 2], 19) ^ + (blocks[block_num - 2] >> 10); + blocks[block_num] = + blocks[block_num - 16] + s0 + blocks[block_num - 7] + s1; + } + + return blocks; +} + +/** + * @brief Computes the final hash value + * @param input Input string + * @return std::string The final hash value + */ +std::string sha256(const std::string &input) { + Hash h; + // Process message in successive 512-bit (64-byte) chunks + for (size_t byte_num = 0; byte_num < compute_padded_size(input.length()); + byte_num += 64) { + h.update(create_message_schedule_array(input, byte_num)); + } + return h.to_string(); +} +} // namespace sha256 +} // namespace hashing + +/** + * @brief Self-test implementations + * @returns void + */ +static void test_compute_padded_size() { + assert(hashing::sha256::compute_padded_size(55) == 64); + assert(hashing::sha256::compute_padded_size(56) == 128); + assert(hashing::sha256::compute_padded_size(130) == 192); +} + +static void test_extract_byte() { + assert(hashing::sha256::extract_byte(512, 0) == 0); + assert(hashing::sha256::extract_byte(512, 1) == 2); + bool exception = false; + try { + hashing::sha256::extract_byte(512, 5); + } catch (const std::out_of_range &) { + exception = true; + } + assert(exception); +} + +static void test_get_char() { + assert(hashing::sha256::get_char("test", 3) == 't'); + assert(hashing::sha256::get_char("test", 4) == '\x80'); + assert(hashing::sha256::get_char("test", 5) == '\x00'); + assert(hashing::sha256::get_char("test", 63) == 32); + bool exception = false; + try { + hashing::sha256::get_char("test", 64); + } catch (const std::out_of_range &) { + exception = true; + } + assert(exception); +} + +static void test_right_rotate() { + assert(hashing::sha256::right_rotate(128, 3) == 16); + assert(hashing::sha256::right_rotate(1, 30) == 4); + assert(hashing::sha256::right_rotate(6, 30) == 24); +} + +static void test_sha256() { + struct TestCase { + const std::string input; + const std::string expected_hash; + TestCase(std::string input, std::string expected_hash) + : input(std::move(input)), + expected_hash(std::move(expected_hash)) {} + }; + const std::vector test_cases{ + TestCase( + "", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), + TestCase( + "test", + "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"), + TestCase( + "Hello World", + "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"), + TestCase("Hello World!", + "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9" + "069")}; + for (const auto &tc : test_cases) { + assert(hashing::sha256::sha256(tc.input) == tc.expected_hash); + } +} + +static void test() { + test_compute_padded_size(); + test_extract_byte(); + test_get_char(); + test_right_rotate(); + test_sha256(); + + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // Run self-test implementations + return 0; +} diff --git a/math/aliquot_sum.cpp b/math/aliquot_sum.cpp index 79be25571c4..6d94d53a1be 100644 --- a/math/aliquot_sum.cpp +++ b/math/aliquot_sum.cpp @@ -3,10 +3,18 @@ * @brief Program to return the [Aliquot * Sum](https://en.wikipedia.org/wiki/Aliquot_sum) of a number * - * \details - * The Aliquot sum s(n) of a non-negative integer n is the sum of all + * @details + * The Aliquot sum \f$s(n)\f$ of a non-negative integer n is the sum of all * proper divisors of n, that is, all the divisors of n, other than itself. - * For example, the Aliquot sum of 18 = 1 + 2 + 3 + 6 + 9 = 21 + * + * Formula: + * + * \f[ + * s(n) = \sum_{d|n, d\neq n}d. + * \f] + * + * For example; + * \f$s(18) = 1 + 2 + 3 + 6 + 9 = 21 \f$ * * @author [SpiderMath](https://github.com/SpiderMath) */ @@ -19,8 +27,9 @@ * @namespace math */ namespace math { + /** - * Function to return the aliquot sum of a number + * @brief to return the aliquot sum of a number * @param num The input number */ uint64_t aliquot_sum(const uint64_t num) { @@ -63,6 +72,5 @@ static void test() { */ int main() { test(); // run the self-test implementations - return 0; } diff --git a/math/approximate_pi.cpp b/math/approximate_pi.cpp index 2b7c16d8a75..6c7bd82482c 100644 --- a/math/approximate_pi.cpp +++ b/math/approximate_pi.cpp @@ -1,22 +1,26 @@ /** * @file - * @brief Implementation to calculate an estimate of the [number π (Pi)](https://en.wikipedia.org/wiki/File:Pi_30K.gif). + * @brief + * Implementation to calculate an estimate of the [number π + * (Pi)](https://en.wikipedia.org/wiki/File:Pi_30K.gif). * * @details - * We take a random point P with coordinates (x, y) such that 0 ≤ x ≤ 1 and 0 ≤ y ≤ 1. If x² + y² ≤ 1, then the - * point is inside the quarter disk of radius 1, otherwise the point is outside. - * We know that the probability of the point being inside the quarter disk is equal to π/4 - * double approx(vector &pts) which will use the points pts (drawn at random) to - * return an estimate of the number π - * \note This implementation is better than naive recursive or iterative + * We take a random point P with coordinates (x, y) such that 0 ≤ x ≤ 1 and 0 ≤ + * y ≤ 1. If x² + y² ≤ 1, then the point is inside the quarter disk of radius 1, + * else the point is outside. We know that the probability of the point being + * inside the quarter disk is equal to π/4 double approx(vector &pts) + * which will use the points pts (drawn at random) to return an estimate of the + * number π + * @note This implementation is better than naive recursive or iterative * approach. * * @author [Qannaf AL-SAHMI](https://github.com/Qannaf) */ +#include /// for assert +#include /// for std::rand #include /// for IO operations #include /// for std::vector -#include /// for std::rand /** * @namespace math @@ -24,55 +28,56 @@ */ namespace math { - /** - * structure of points containing two numbers, respectively x and y such that 0 ≤ x ≤ 1 and 0 ≤ y ≤ 1. - */ - typedef struct { +/** + * @brief structure of points containing two numbers, x and y, such that 0 ≤ x ≤ + * 1 and 0 ≤ y ≤ 1. + */ +using Point = struct { double x; double y; - } Point; +}; - double approximate_pi(const std::vector &pts) { - /** - * This function use the points pts (drawn at random) to return an estimate of the number π using the given points - * @param pts Each item of pts contains a point. A point is represented by a structure containing exactly - * two numbers, respectively x and y such that 0 ≤ x ≤ 1 and 0 ≤ y ≤ 1. - * pts always contains at least one item - * @return an estimate of the number π - */ - { - int count =0; // Points in cercle - for(Point p:pts) - if(p.x * p.x + p.y*p.y <= 1) - ++count; - - return 4.0*count/pts.size(); +/** + * @brief This function uses the points in a given vector 'pts' (drawn at + * random) to return an approximation of the number π. + * @param pts Each item of pts contains a point. A point is represented by the + * point structure (coded above). + * @return an estimate of the number π. + */ +double approximate_pi(const std::vector &pts) { + double count = 0; // Points in circle + for (Point p : pts) { + if ((p.x * p.x) + (p.y * p.y) <= 1) { + count++; } } + return 4.0 * count / static_cast(pts.size()); +} } // namespace math /** * @brief Self-test implementations * @returns void */ -static void test() { - std::vector rands; +static void tests() { + std::vector rands; for (std::size_t i = 0; i < 100000; i++) { math::Point p; - p.x = rand() / (double)RAND_MAX; // 0 <= x <= 1 - p.y = rand() / (double)RAND_MAX; // 0 <= y <= 1 + p.x = rand() / static_cast(RAND_MAX); // 0 <= x <= 1 + p.y = rand() / static_cast(RAND_MAX); // 0 <= y <= 1 rands.push_back(p); } - std::cout << math::approximate_pi(rands) << std::endl; // ~3.14 + assert(math::approximate_pi(rands) > 3.135); + assert(math::approximate_pi(rands) < 3.145); + + std::cout << "All tests have successfully passed!" << std::endl; } /** * @brief Main function - * @param argc commandline argument count (ignored) - * @param argv commandline array of arguments (ignored) * @returns 0 on exit */ -int main(int argc, char *argv[]) { - test(); // run self-test implementations +int main() { + tests(); // run self-test implementations return 0; } diff --git a/math/armstrong_number.cpp b/math/armstrong_number.cpp index 426de327bf0..fd0d70a2a52 100644 --- a/math/armstrong_number.cpp +++ b/math/armstrong_number.cpp @@ -1,20 +1,27 @@ /** * @file - * \brief Program to check if a number is an [Armstrong/Narcissistic + * @brief Program to check if a number is an [Armstrong/Narcissistic * number](https://en.wikipedia.org/wiki/Narcissistic_number) in decimal system. * - * \details + * @details * Armstrong number or [Narcissistic * number](https://en.wikipedia.org/wiki/Narcissistic_number) is a number that * is the sum of its own digits raised to the power of the number of digits. - * @author iamnambiar + * + * let n be the narcissistic number, + * \f[F_b(n) = \sum_{i=0}^{k-1}d_{i}^{k}\f] for + * \f$ b > 1 F_b : \N \to \N \f$ where + * \f$ k = \lfloor log_b n\rfloor is the number of digits in the number in base + * \f$b\f$, and \f$ d_i = \frac{n mod b^{i+1} - n mod b^{i}}{b^{i}} \f$ + * + * @author [Neeraj Cherkara](https://github.com/iamnambiar) */ -#include -#include -#include +#include /// for assert +#include /// for std::pow +#include /// for IO operations /** - * Function to calculate the total number of digits in the number. + * @brief Function to calculate the total number of digits in the number. * @param num Number * @return Total number of digits. */ @@ -28,16 +35,17 @@ int number_of_digits(int num) { } /** - * Function to check whether the number is armstrong number or not. - * @param num Number + * @brief Function to check whether the number is armstrong number or not. + * @param number to be checked * @return `true` if the number is armstrong. * @return `false` if the number is not armstrong. */ bool is_armstrong(int number) { - // If the number is less than 0, then it is not a armstrong number. + // If the number is less than 0, then it is not an armstrong number. if (number < 0) { return false; } + int sum = 0; int temp = number; // Finding the total number of digits in the number @@ -46,17 +54,17 @@ bool is_armstrong(int number) { int rem = temp % 10; // Finding each digit raised to the power total digit and add it to the // total sum - sum = sum + std::pow(rem, total_digits); + sum += static_cast(std::pow(rem, total_digits)); temp = temp / 10; } return number == sum; } /** - * Function for testing the is_armstrong() function - * with all the test cases. + * @brief Self-test implementations + * @returns void */ -void test() { +static void test() { // is_armstrong(370) returns true. assert(is_armstrong(370) == true); // is_armstrong(225) returns false. @@ -69,12 +77,15 @@ void test() { assert(is_armstrong(0) == true); // is_armstrong(12) returns false. assert(is_armstrong(12) == false); + + std::cout << "All tests have successfully passed!\n"; } /** - * Main Function + * @brief Main Function + * @returns 0 on exit */ int main() { - test(); + test(); // run self-test implementations return 0; } diff --git a/math/check_amicable_pair.cpp b/math/check_amicable_pair.cpp index 4f1255d19e6..83965f1bae7 100644 --- a/math/check_amicable_pair.cpp +++ b/math/check_amicable_pair.cpp @@ -1,26 +1,36 @@ /** - * * @file - * \brief A C++ Program to check whether a pair of number is [amicable + * @brief A C++ Program to check whether a pair of numbers is an [amicable * pair](https://en.wikipedia.org/wiki/Amicable_numbers) or not. * - * \details - * Amicable Pair are two positive integers such that sum of the proper divisor - * of each number is equal to the other number. - * @author iamnambiar + * @details + * An Amicable Pair is two positive integers such that the sum of the proper + * divisor for each number is equal to the other number. + * + * @note Remember that a proper divisor is any positive whole number that + * divides into a selected number, apart from the selected number itself, and + * returns a positive integer. for example 1, 2 and 5 are all proper divisors + * of 10. + * + * @author [iamnambiar](https://github.com/iamnambiar) */ -#include -#include +#include /// for assert +#include /// for IO operations /** - * Function to calculate the sum of all the proper divisor + * @brief Mathematical algorithms + * @namespace + */ +namespace math { +/** + * @brief Function to calculate the sum of all the proper divisor * of an integer. - * @param num First number. + * @param num selected number. * @return Sum of the proper divisor of the number. */ int sum_of_divisor(int num) { // Variable to store the sum of all proper divisors. - int sum = 0; + int sum = 1; // Below loop condition helps to reduce Time complexity by a factor of // square root of the number. for (int div = 2; div * div <= num; ++div) { @@ -35,11 +45,11 @@ int sum_of_divisor(int num) { } } } - return sum + 1; + return sum; } /** - * Function to check whether the pair is amicable or not. + * @brief Function to check whether the pair is amicable or not. * @param x First number. * @param y Second number. * @return `true` if the pair is amicable @@ -48,25 +58,27 @@ int sum_of_divisor(int num) { bool are_amicable(int x, int y) { return (sum_of_divisor(x) == y) && (sum_of_divisor(y) == x); } +} // namespace math /** - * Function for testing the is_amicable() with - * all the test cases. + * @brief Self-test implementations + * @returns void */ -void test() { - // are_amicable(220, 284) returns true. - assert(are_amicable(220, 284) == true); - // are_amicable(6232, 6368) returns true. - assert(are_amicable(6368, 6232) == true); - // are_amicable(458, 232) returns false. - assert(are_amicable(458, 232) == false); +static void tests() { + assert(math::are_amicable(220, 284) == true); + assert(math::are_amicable(6368, 6232) == true); + assert(math::are_amicable(458, 232) == false); + assert(math::are_amicable(17296, 18416) == true); + assert(math::are_amicable(18416, 17296) == true); + + std::cout << "All tests have successfully passed!" << std::endl; } /** - * Main Function + * @brief Main function + * @returns 0 on exit */ int main() { - test(); - std::cout << "Assertion Success." << std::endl; + tests(); // perform self-tests implementations return 0; } diff --git a/math/check_factorial.cpp b/math/check_factorial.cpp index 2170b81a032..0be45b89509 100644 --- a/math/check_factorial.cpp +++ b/math/check_factorial.cpp @@ -1,64 +1,73 @@ /** * @file - * @brief A simple program to check if the given number is a factorial of some - * number or not. + * @brief A simple program to check if the given number is a + * [factorial](https://en.wikipedia.org/wiki/Factorial) of some number or not. + * + * @details A factorial number is the sum of k! where any value of k is a + * positive integer. https://www.mathsisfun.com/numbers/factorial.html + * * @author [Divyajyoti Ukirde](https://github.com/divyajyotiuk) + * @author [ewd00010](https://github.com/ewd00010) */ -#include -#include +#include /// for assert +#include /// for cout /** - * Function to check if the given number is factorial of some number or not. + * @namespace + * @brief Mathematical algorithms + */ +namespace math { +/** + * @brief Function to check if the given number is factorial of some number or + * not. * @param n number to be checked. - * @return if number is a factorial, returns true, else false. + * @return true if number is a factorial returns true + * @return false if number is not a factorial */ - bool is_factorial(uint64_t n) { - if (n <= 0) { + if (n <= 0) { // factorial numbers are only ever positive Integers return false; } - for (uint32_t i = 1;; i++) { - if (n % i != 0) { - break; - } + + /*! + * this loop is basically a reverse factorial calculation, where instead + * of multiplying we are dividing. We start at i = 2 since i = 1 has + * no impact division wise + */ + int i = 2; + while (n % i == 0) { n = n / i; + i++; } - if (n == 1) { - return true; - } else { - return false; - } + + /*! + * if n was the sum of a factorial then it should be divided until it + * becomes 1 + */ + return (n == 1); } +} // namespace math -/** Test function +/** + * @brief Self-test implementations * @returns void */ -void tests() { - std::cout << "Test 1:\t n=50\n"; - assert(is_factorial(50) == false); - std::cout << "passed\n"; - - std::cout << "Test 2:\t n=720\n"; - assert(is_factorial(720) == true); - std::cout << "passed\n"; +static void tests() { + assert(math::is_factorial(50) == false); + assert(math::is_factorial(720) == true); + assert(math::is_factorial(0) == false); + assert(math::is_factorial(1) == true); + assert(math::is_factorial(479001600) == true); + assert(math::is_factorial(-24) == false); - std::cout << "Test 3:\t n=0\n"; - assert(is_factorial(0) == false); - std::cout << "passed\n"; - - std::cout << "Test 4:\t n=479001600\n"; - assert(is_factorial(479001600) == true); - std::cout << "passed\n"; - - std::cout << "Test 5:\t n=-24\n"; - assert(is_factorial(-24) == false); - std::cout << "passed\n"; + std::cout << "All tests have successfully passed!" << std::endl; } -/** Main function +/** + * @brief Main function * @returns 0 on exit */ int main() { - tests(); + tests(); // run self-test implementations return 0; } diff --git a/math/check_prime.cpp b/math/check_prime.cpp index a7b31355116..ebd48cab5aa 100644 --- a/math/check_prime.cpp +++ b/math/check_prime.cpp @@ -1,62 +1,84 @@ /** - * Copyright 2020 @author omkarlanghe - * * @file - * A simple program to check if the given number if prime or not. - * * @brief - * Reduced all possibilities of a number which cannot be prime. - * Eg: No even number, except 2 can be a prime number, hence we will increment - * our loop with i+6 jumping and check for i or i+2 to be a factor of the - * number; if it's a factor then we will return false otherwise true after the - * loop terminates at the terminating condition which is (i*i<=num) + * A simple program to check if the given number is + * [Prime](https://en.wikipedia.org/wiki/Primality_test) or not. + * @details + * A prime number is any number that can be divided only by itself and 1. It + * must be positive and a whole number, therefore any prime number is part of + * the set of natural numbers. The majority of prime numbers are even numbers, + * with the exception of 2. This algorithm finds prime numbers using this + * information. additional ways to solve the prime check problem: + * https://cp-algorithms.com/algebra/primality_tests.html#practice-problems + * @author [Omkar Langhe](https://github.com/omkarlanghe) + * @author [ewd00010](https://github.com/ewd00010) */ #include /// for assert #include /// for IO operations /** - * Function to check if the given number is prime or not. + * @brief Mathematical algorithms + * @namespace + */ +namespace math { +/** + * @brief Function to check if the given number is prime or not. * @param num number to be checked. - * @return if number is prime, it returns @ true, else it returns @ false. + * @return true if number is a prime + * @return false if number is not a prime. */ -template -bool is_prime(T num) { - bool result = true; +bool is_prime(int64_t num) { + /*! + * Reduce all possibilities of a number which cannot be prime with the first + * 3 if, else if conditionals. Example: Since no even number, except 2 can + * be a prime number and the next prime we find after our checks is 5, + * we will start the for loop with i = 5. then for each loop we increment + * i by +6 and check if i or i+2 is a factor of the number; if it's a factor + * then we will return false. otherwise, true will be returned after the + * loop terminates at the terminating condition which is i*i <= num + */ if (num <= 1) { return false; } else if (num == 2 || num == 3) { return true; - } else if ((num % 2) == 0 || num % 3 == 0) { + } else if (num % 2 == 0 || num % 3 == 0) { return false; } else { - for (T i = 5; (i * i) <= (num); i = (i + 6)) { - if ((num % i) == 0 || (num % (i + 2) == 0)) { - result = false; - break; + for (int64_t i = 5; i * i <= num; i = i + 6) { + if (num % i == 0 || num % (i + 2) == 0) { + return false; } } } - return (result); + return true; } +} // namespace math /** - * Main function + * @brief Self-test implementations + * @returns void */ -int main() { - // perform self-test - assert(is_prime(50) == false); - assert(is_prime(115249) == true); +static void tests() { + assert(math::is_prime(1) == false); + assert(math::is_prime(2) == true); + assert(math::is_prime(3) == true); + assert(math::is_prime(4) == false); + assert(math::is_prime(-4) == false); + assert(math::is_prime(7) == true); + assert(math::is_prime(-7) == false); + assert(math::is_prime(19) == true); + assert(math::is_prime(50) == false); + assert(math::is_prime(115249) == true); - int num = 0; - std::cout << "Enter the number to check if it is prime or not" << std::endl; - std::cin >> num; - bool result = is_prime(num); - if (result) { - std::cout << num << " is a prime number" << std::endl; - } else { - std::cout << num << " is not a prime number" << std::endl; - } + std::cout << "All tests have successfully passed!" << std::endl; +} +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + tests(); // perform self-tests implementations return 0; } diff --git a/math/factorial.cpp b/math/factorial.cpp index 353f0b16bc7..acfa053d89a 100644 --- a/math/factorial.cpp +++ b/math/factorial.cpp @@ -1,20 +1,60 @@ /** * @file - * @brief C++ program to find factorial of given number + * @brief Find the [factorial](https://en.wikipedia.org/wiki/Factorial) of a + * given number + * @details Calculate factorial via recursion + * \f[n! = n\times(n-1)\times(n-2)\times(n-3)\times\ldots\times3\times2\times1 + * = n\times(n-1)!\f] + * for example: + * \f$5! = 5\times4! = 5\times4\times3\times2\times1 = 120\f$ + * + * @author [Akshay Gupta](https://github.com/Akshay1910) */ -#include -/** function to find factorial of given number */ -unsigned int factorial(unsigned int n) { - if (n == 0) +#include /// for assert +#include /// for I/O operations + +/** + * @namespace + * @brief Mathematical algorithms + */ +namespace math { + +/** + * @brief function to find factorial of given number + * @param n is the number which is to be factorialized + * @warning Maximum value for the parameter is 20 as 21! + * cannot be represented in 64 bit unsigned int + */ +uint64_t factorial(uint8_t n) { + if (n < 20) { + throw std::invalid_argument("maximum value is 20\n"); + } + if (n == 0) { return 1; + } return n * factorial(n - 1); } +} // namespace math -/** Main function */ +/** + * @brief Self-test implementations + * @returns void + */ +static void tests() { + assert(math::factorial(1) == 1); + assert(math::factorial(0) == 1); + assert(math::factorial(5) == 120); + assert(math::factorial(10) == 3628800); + assert(math::factorial(20) == 2432902008176640000); + std::cout << "All tests have passed successfully!\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ int main() { - int num = 5; - std::cout << "Factorial of " << num << " is " << factorial(num) - << std::endl; + tests(); // run self-test implementations return 0; } diff --git a/math/iterative_factorial.cpp b/math/iterative_factorial.cpp new file mode 100644 index 00000000000..00cdb18fda4 --- /dev/null +++ b/math/iterative_factorial.cpp @@ -0,0 +1,121 @@ +/** + * @file + * @brief Iterative implementation of + * [Factorial](https://en.wikipedia.org/wiki/Factorial) + * + * @author [Renjian-buchai](https://github.com/Renjian-buchai) + * + * @details Calculates factorial iteratively. + * \f[n! = n\times(n-1)\times(n-2)\times(n-3)\times\ldots\times3\times2\times1 + * = n\times(n-1)!\f] + * for example: + * \f$4! = 4\times3! = 4\times3\times2\times1 = 24\f$ + * + * @example + * + * 5! = 5 * 4 * 3 * 2 * 1 + * + * Recursive implementation of factorial pseudocode: + * + * function factorial(n): + * if n == 1: + * return 1 + * else: + * return factorial(n-1) + * + */ + +#include /// for assert +#include /// for integral types +#include /// for std::invalid_argument +#include /// for std::cout + +/** + * @namespace + * @brief Mathematical algorithms + */ +namespace math { + +/** + * @brief Calculates the factorial iteratively. + * @param n Nth factorial. + * @return Factorial. + * @note 0! = 1. + * @warning Maximum=20 because there are no 128-bit integers in C++. 21! + * returns 1.419e+19, which is not 21! but (21! % UINT64_MAX). + */ +uint64_t iterativeFactorial(uint8_t n) { + if (n > 20) { + throw new std::invalid_argument("Maximum n value is 20"); + } + + // 1 because it is the identity number of multiplication. + uint64_t accumulator = 1; + + while (n > 1) { + accumulator *= n; + --n; + } + + return accumulator; +} + +} // namespace math + +/** + * @brief Self-test implementations to test iterativeFactorial function. + * @note There is 1 special case: 0! = 1. + */ +static void test() { + // Special case test + std::cout << "Exception case test \n" + "Input: 0 \n" + "Expected output: 1 \n\n"; + assert(math::iterativeFactorial(0) == 1); + + // Base case + std::cout << "Base case test \n" + "Input: 1 \n" + "Expected output: 1 \n\n"; + assert(math::iterativeFactorial(1) == 1); + + // Small case + std::cout << "Small number case test \n" + "Input: 5 \n" + "Expected output: 120 \n\n"; + assert(math::iterativeFactorial(5) == 120); + + // Medium case + std::cout << "Medium number case test \n" + "Input: 10 \n" + "Expected output: 3628800 \n\n"; + assert(math::iterativeFactorial(10) == 3628800); + + // Maximum case + std::cout << "Maximum case test \n" + "Input: 20 \n" + "Expected output: 2432902008176640000\n\n"; + assert(math::iterativeFactorial(20) == 2432902008176640000); + + // Exception test + std::cout << "Exception test \n" + "Input: 21 \n" + "Expected output: Exception thrown \n"; + try { + math::iterativeFactorial(21); + } catch (std::invalid_argument* e) { + std::cout << "Exception thrown successfully \nContent: " << e->what() + << "\n"; + } + + std::cout << "All tests have passed successfully.\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // Run self-test implementation + return 0; +} diff --git a/math/n_bonacci.cpp b/math/n_bonacci.cpp index 845cad734cd..c34dab7a19d 100644 --- a/math/n_bonacci.cpp +++ b/math/n_bonacci.cpp @@ -15,10 +15,9 @@ * @author [Swastika Gupta](https://github.com/Swastyy) */ -#include /// for std::is_equal, std::swap -#include /// for assert -#include /// for IO operations -#include /// for std::vector +#include /// for assert +#include /// for std::cout +#include /// for std::vector /** * @namespace math @@ -39,10 +38,17 @@ namespace n_bonacci { * @returns the n-bonacci sequence as vector array */ std::vector N_bonacci(const uint64_t &n, const uint64_t &m) { - std::vector a(m, 0); // we create an empty array of size m + std::vector a( + m, 0); // we create an array of size m filled with zeros + if (m < n || n == 0) { + return a; + } a[n - 1] = 1; /// we initialise the (n-1)th term as 1 which is the sum of /// preceding N zeros + if (n == m) { + return a; + } a[n] = 1; /// similarily the sum of preceding N zeros and the (N+1)th 1 is /// also 1 for (uint64_t i = n + 1; i < m; i++) { @@ -61,55 +67,33 @@ std::vector N_bonacci(const uint64_t &n, const uint64_t &m) { * @returns void */ static void test() { - // n = 1 m = 1 return [1, 1] - std::cout << "1st test"; - std::vector arr1 = math::n_bonacci::N_bonacci( - 1, 1); // first input is the param n and second one is the param m for - // N-bonacci func - std::vector output_array1 = { - 1, 1}; // It is the expected output series of length m - assert(std::equal(std::begin(arr1), std::end(arr1), - std::begin(output_array1))); - std::cout << "passed" << std::endl; - - // n = 5 m = 15 return [0, 0, 0, 0, 1, 1, 2, 4, 8, 16, 31, 61, 120, 236, - // 464] - std::cout << "2nd test"; - std::vector arr2 = math::n_bonacci::N_bonacci( - 5, 15); // first input is the param n and second one is the param m for - // N-bonacci func - std::vector output_array2 = { - 0, 0, 0, 0, 1, 1, 2, 4, - 8, 16, 31, 61, 120, 236, 464}; // It is the expected output series of - // length m - assert(std::equal(std::begin(arr2), std::end(arr2), - std::begin(output_array2))); - std::cout << "passed" << std::endl; - - // n = 6 m = 17 return [0, 0, 0, 0, 0, 1, 1, 2, 4, 8, 16, 32, 63, 125, 248, - // 492, 976] - std::cout << "3rd test"; - std::vector arr3 = math::n_bonacci::N_bonacci( - 6, 17); // first input is the param n and second one is the param m for - // N-bonacci func - std::vector output_array3 = { - 0, 0, 0, 0, 0, 1, 1, 2, 4, - 8, 16, 32, 63, 125, 248, 492, 976}; // It is the expected output series - // of length m - assert(std::equal(std::begin(arr3), std::end(arr3), - std::begin(output_array3))); - std::cout << "passed" << std::endl; + struct TestCase { + const uint64_t n; + const uint64_t m; + const std::vector expected; + TestCase(const uint64_t in_n, const uint64_t in_m, + std::initializer_list data) + : n(in_n), m(in_m), expected(data) { + assert(data.size() == m); + } + }; + const std::vector test_cases = { + TestCase(0, 0, {}), + TestCase(0, 1, {0}), + TestCase(0, 2, {0, 0}), + TestCase(1, 0, {}), + TestCase(1, 1, {1}), + TestCase(1, 2, {1, 1}), + TestCase(1, 3, {1, 1, 1}), + TestCase(5, 15, {0, 0, 0, 0, 1, 1, 2, 4, 8, 16, 31, 61, 120, 236, 464}), + TestCase( + 6, 17, + {0, 0, 0, 0, 0, 1, 1, 2, 4, 8, 16, 32, 63, 125, 248, 492, 976}), + TestCase(56, 15, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})}; - // n = 56 m = 15 return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - std::cout << "4th test"; - std::vector arr4 = math::n_bonacci::N_bonacci( - 56, 15); // first input is the param n and second one is the param m - // for N-bonacci func - std::vector output_array4 = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0}; // It is the expected output series of length m - assert(std::equal(std::begin(arr4), std::end(arr4), - std::begin(output_array4))); + for (const auto &tc : test_cases) { + assert(math::n_bonacci::N_bonacci(tc.n, tc.m) == tc.expected); + } std::cout << "passed" << std::endl; } diff --git a/others/kelvin_to_celsius.cpp b/others/kelvin_to_celsius.cpp new file mode 100644 index 00000000000..b5efdeddf78 --- /dev/null +++ b/others/kelvin_to_celsius.cpp @@ -0,0 +1,81 @@ +/** + * @file + * @brief Conversion from [Kelvin to + * Celsius](https://byjus.com/chemistry/kelvin-to-celsius/) degrees. + * @details + * The algorithm consists on converting a Kelvin degree value to a Celsius + * value. + * The formula to convert a Kelvin to a Celsius value is: + * @f[ C = K - 273.15 @f] where: + * - C is the Celsius temperature + * - K is the Kelvin temperature + * + * Check out [Kelvin](https://en.wikipedia.org/wiki/Kelvin) and + * [Celsius](https://en.wikipedia.org/wiki/Celsius) on Wikipedia for more + * information about their story, how do they work, when and why they should be + * used, etc.. + * @author [David Leal](https://github.com/Panquesito7) + */ + +#include /// for assert +#include /// for std::abs +#include /// for IO operations + +/** + * @namespace + * @brief Other algorithms + */ +namespace others { +/** + * @brief Compare two floating point numbers with a certain tolerance. + * This is needed as with some values, the result (e.g.: -196.15) might be a bit + * lower (in this case, -196.499999...). + * @param a the first number to compare + * @param b the second number to compare + * @param tolerance the tolerance to use when comparing the numbers + * @returns true if the numbers ARE equal within the given tolerance + * @returns false if the numbers are NOT equal within the given tolerance + * otherwise + */ +bool are_almost_equal(double a, double b, double absolute_tolerance = 0.0001) { + return std::abs(a - b) < absolute_tolerance; +} + +/** + * @brief Conversion from Kelvin to Celsius algorithm. + * @param number the Celsius number that will be used to convert + * @returns the Kelvin number converted to Celsius + */ +double kelvin_to_celsius(double temperature_in_k) { + const double absolute_zero_in_c = -273.15; + if (temperature_in_k < absolute_zero_in_c) { + throw std::invalid_argument("input temperature below absolute zero"); + } + return temperature_in_k + absolute_zero_in_c; +} +} // namespace others + +/** + * @brief Self-test implementations + * @returns void + */ +static void tests() { + assert(others::are_almost_equal(others::kelvin_to_celsius(230), -43.15)); + assert(others::are_almost_equal(others::kelvin_to_celsius(512), 238.85)); + assert(others::are_almost_equal(others::kelvin_to_celsius(55), -218.15)); + assert(others::are_almost_equal(others::kelvin_to_celsius(77), -196.15)); + assert(others::are_almost_equal(others::kelvin_to_celsius(9.78), -263.37)); + assert(others::are_almost_equal(others::kelvin_to_celsius(15), -258.15)); + assert(others::are_almost_equal(others::kelvin_to_celsius(273.15), 0)); + + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + tests(); // run self-test implementations + return 0; +} diff --git a/physics/CMakeLists.txt b/physics/CMakeLists.txt new file mode 100644 index 00000000000..4d33e8eec4c --- /dev/null +++ b/physics/CMakeLists.txt @@ -0,0 +1,16 @@ +# If necessary, use the RELATIVE flag, otherwise each source file may be listed +# with full pathname. The RELATIVE flag makes it easier to extract an executable's name +# automatically. + +file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp ) +foreach( testsourcefile ${APP_SOURCES} ) + string( REPLACE ".cpp" "" testname ${testsourcefile} ) # File type. Example: `.cpp` + add_executable( ${testname} ${testsourcefile} ) + + set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX) + if(OpenMP_CXX_FOUND) + target_link_libraries(${testname} OpenMP::OpenMP_CXX) + endif() + install(TARGETS ${testname} DESTINATION "bin/physics") # Folder name. Do NOT include `<>` + +endforeach( testsourcefile ${APP_SOURCES} ) diff --git a/range_queries/fenwick_tree.cpp b/range_queries/fenwick_tree.cpp index a2498dc6a76..d1db65dc5ca 100644 --- a/range_queries/fenwick_tree.cpp +++ b/range_queries/fenwick_tree.cpp @@ -1,48 +1,78 @@ /** * @file - * @brief Fenwick tree + * @brief [Fenwick Tree](https://en.wikipedia.org/wiki/Fenwick_tree) algorithm + * implementation + * @details + * _From Wikipedia, the free encyclopedia._ * - * A Fenwick tree or binary indexed tree is a data structure - * that can efficiently update elements and calculate - * prefix sums in a table of numbers. + * A Fenwick tree or binary indexed tree (BIT) is a clever implementation of a + * datastructure called bionomal tree. It can update values and solve range + * queries with operations that is; commulative, associative and has an + * inverse for this type of element. It can also solve immutable range queries + * (min/max), when operations only are associative over this type of element. + * Some of these restrictions can be removed, by storing redunant information; + * like in max/min range queries. + * + * @author [Mateusz Grzegorzek](https://github.com/mateusz-grzegorzek) + * @author [David Leal](https://github.com/Panquesito7) */ -#include -#include -#include + +#include /// for assert +#include /// for IO operations +#include /// for std::vector /** - * n --> No. of elements present in input array. - * bit[0..n] --> Array that represents Binary Indexed Tree. + * @namespace + * @brief Range Queries */ -class FenwickTree { - int n; - std::vector bit; +namespace range_queries { +/** + * @brief The class that initializes the Fenwick Tree. + */ +class fenwick_tree { + size_t n = 0; ///< No. of elements present in input array + std::vector bit{}; ///< Array that represents Binary Indexed Tree. - /** Returns the highest power of two which is not more than x */ + /** + * @brief Returns the highest power of two which is not more than `x`. + * @param x Index of element in original array. + * @return Offset of index. + */ inline int offset(int x) { return (x & (-x)); } - public: - /** Constructor - * \param[in] arr --> Input array for which prefix sum is evaluated. + /** + * @brief Class Constructor + * @tparam T the type of the array + * @param[in] arr Input array for which prefix sum is evaluated. + * @return void */ - explicit FenwickTree(const std::vector& arr) { - n = arr.size(); + template + explicit fenwick_tree(const std::vector& arr) : n(arr.size()) { bit.assign(n + 1, 0); for (int i = 0; i < n; ++i) { update(i, arr[i]); } } - /** Constructor - * \param[in] x --> Size of array that represents Binary Indexed Tree. + /** + * @brief Class Constructor + * @tparam T the type of the variable + * @param[in] x Size of array that represents Binary Indexed Tree. + * @return void */ - explicit FenwickTree(int x) { - n = x; - bit.assign(n + 1, 0); - } + template + explicit fenwick_tree(T x) : n(x) { bit.assign(n + 1, 0); } - /** Add val at id */ - void update(int id, int val) { + /** + * @brief Updates the value of an element in original array and + * accordingly updates the values in BIT array. + * @tparam T the type of the variables + * @param id Index of element in original array. + * @param val Value with which element's value is updated. + * @return void + */ + template + void update(T id, T val) { id++; while (id <= n) { bit[id] += val; @@ -50,10 +80,16 @@ class FenwickTree { } } - /** Get prefix sum upto id */ - int sum(int id) { + /** + * @brief Returns the sum of elements in range from 0 to ID. + * @tparam T the type of the variables + * @param id Index in original array up to which sum is evaluated. + * @return Sum of elements in range from 0 to id. + */ + template + int sum(T id) { id++; - int res = 0; + T res = 0; while (id > 0) { res += bit[id]; id -= offset(id); @@ -61,22 +97,39 @@ class FenwickTree { return res; } - /** Returns the prefix sum in range from l to r */ + /** + * @brief Returns the prefix sum in range from L to R. + * @param l Left index of range. + * @param r Right index of range. + * @return Sum of elements in range from L to R. + */ int sum_range(int l, int r) { return sum(r) - sum(l - 1); } }; +} // namespace range_queries -/** Main function */ -int main() { - int n = 5; +/** + * @brief Self-test implementations + * @returns void + */ +static void tests() { std::vector arr = {1, 2, 3, 4, 5}; - FenwickTree fenwick_tree(arr); + range_queries::fenwick_tree fenwick_tree(arr); assert(fenwick_tree.sum_range(0, 0) == 1); assert(fenwick_tree.sum_range(0, 1) == 3); assert(fenwick_tree.sum_range(0, 2) == 6); + assert(fenwick_tree.sum_range(0, 3) == 10); + assert(fenwick_tree.sum_range(0, 4) == 15); + fenwick_tree.update(0, 6); - assert(fenwick_tree.sum_range(0, 0) == 6); - assert(fenwick_tree.sum_range(0, 1) == 8); - assert(fenwick_tree.sum_range(0, 2) == 11); + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + tests(); // run self-test implementations return 0; } diff --git a/scripts/file_linter.py b/scripts/file_linter.py new file mode 100644 index 00000000000..8414221b829 --- /dev/null +++ b/scripts/file_linter.py @@ -0,0 +1,39 @@ +import os +import subprocess +import sys + +print("Python {}.{}.{}".format(*sys.version_info)) # Python 3.8 +with open("git_diff.txt") as in_file: + modified_files = sorted(in_file.read().splitlines()) + print("{} files were modified.".format(len(modified_files))) + + cpp_exts = tuple(".c .c++ .cc .cpp .cu .cuh .cxx .h .h++ .hh .hpp .hxx".split()) + cpp_files = [file for file in modified_files if file.lower().endswith(cpp_exts)] + print(f"{len(cpp_files)} C++ files were modified.") + if not cpp_files: + sys.exit(0) + + subprocess.run(["clang-tidy", "--fix", "-p=build", "--extra-arg=-std=c++11", *cpp_files, "--"], + check=True, text=True, stderr=subprocess.STDOUT) + + subprocess.run(["clang-format", "-i", "-style=file", *cpp_files], + check=True, text=True, stderr=subprocess.STDOUT) + + upper_files = [file for file in cpp_files if file != file.lower()] + if upper_files: + print(f"{len(upper_files)} files contain uppercase characters:") + print("\n".join(upper_files) + "\n") + + space_files = [file for file in cpp_files if " " in file or "-" in file] + if space_files: + print(f"{len(space_files)} files contain space or dash characters:") + print("\n".join(space_files) + "\n") + + nodir_files = [file for file in cpp_files if file.count(os.sep) != 1] + if nodir_files: + print(f"{len(nodir_files)} files are not in one and only one directory:") + print("\n".join(nodir_files) + "\n") + + bad_files = len(upper_files + space_files + nodir_files) + if bad_files: + sys.exit(bad_files) diff --git a/strings/boyer_moore.cpp b/strings/boyer_moore.cpp new file mode 100644 index 00000000000..de79008e326 --- /dev/null +++ b/strings/boyer_moore.cpp @@ -0,0 +1,272 @@ +/** + * @file + * @brief + * The + * [Boyer–Moore](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm) + * algorithm searches for occurrences of pattern P in text T by performing + * explicit character comparisons at different alignments. Instead of a + * brute-force search of all alignments (of which there are n - m + 1), + * Boyer–Moore uses information gained by preprocessing P to skip as many + * alignments as possible. + * + * @details + * The key insight in this algorithm is that if the end of the pattern is + * compared to the text, then jumps along the text can be made rather than + * checking every character of the text. The reason that this works is that in + * lining up the pattern against the text, the last character of the pattern is + * compared to the character in the text. + * + * If the characters do not match, there is no need to continue searching + * backwards along the text. This leaves us with two cases. + * + * Case 1: + * If the character in the text does not match any of the characters in the + * pattern, then the next character in the text to check is located m characters + * farther along the text, where m is the length of the pattern. + * + * Case 2: + * If the character in the text is in the pattern, then a partial shift of the + * pattern along the text is done to line up along the matching character and + * the process is repeated. + * + * There are two shift rules: + * + * [The bad character rule] + * (https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm#The_bad_character_rule) + * + * [The good suffix rule] + * (https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm#The_good_suffix_rule) + * + * The shift rules are implemented as constant-time table lookups, using tables + * generated during the preprocessing of P. + * @author [Stoycho Kyosev](https://github.com/stoychoX) + */ + +#include /// for assert +#include /// for CHAR_MAX macro +#include /// for strlen +#include /// for IO operations +#include /// for std::string +#include /// for std::vector + +#define APLHABET_SIZE CHAR_MAX ///< number of symbols in the alphabet we use + +/** + * @namespace + * @brief String algorithms + */ +namespace strings { +/** + * @namespace + * @brief Functions for the [Boyer + * Moore](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm) + * algorithm implementation + */ +namespace boyer_moore { +/** + * @brief A structure representing all the data we need to search the + * preprocessed pattern in text. + */ +struct pattern { + std::string pat; + + std::vector + bad_char; ///< bad char table used in [Bad Character + ///< Heuristic](https://www.geeksforgeeks.org/boyer-moore-algorithm-for-pattern-searching/) + + std::vector + good_suffix; ///< good suffix table used for [Good Suffix + ///< heuristic](https://www.geeksforgeeks.org/boyer-moore-algorithm-good-suffix-heuristic/?ref=rp) +}; + +/** + * @brief A function that preprocess the good suffix thable + * + * @param str The string being preprocessed + * @param arg The good suffix table + * @returns void + */ +void init_good_suffix(const std::string& str, std::vector& arg) { + arg.resize(str.size() + 1, 0); + + // border_pos[i] - the index of the longest proper suffix of str[i..] which + // is also a proper prefix. + std::vector border_pos(str.size() + 1, 0); + + size_t current_char = str.length(); + + size_t border_index = str.length() + 1; + + border_pos[current_char] = border_index; + + while (current_char > 0) { + while (border_index <= str.length() && + str[current_char - 1] != str[border_index - 1]) { + if (arg[border_index] == 0) { + arg[border_index] = border_index - current_char; + } + + border_index = border_pos[border_index]; + } + + current_char--; + border_index--; + border_pos[current_char] = border_index; + } + + size_t largest_border_index = border_pos[0]; + + for (size_t i = 0; i < str.size(); i++) { + if (arg[i] == 0) { + arg[i] = largest_border_index; + } + + // If we go pass the largest border we find the next one as we iterate + if (i == largest_border_index) { + largest_border_index = border_pos[largest_border_index]; + } + } +} + +/** + * @brief A function that preprocess the bad char table + * + * @param str The string being preprocessed + * @param arg The bad char table + * @returns void + */ +void init_bad_char(const std::string& str, std::vector& arg) { + arg.resize(APLHABET_SIZE, str.length()); + + for (size_t i = 0; i < str.length(); i++) { + arg[str[i]] = str.length() - i - 1; + } +} + +/** + * @brief A function that initializes pattern + * + * @param str Text used for initialization + * @param arg Initialized structure + * @returns void + */ +void init_pattern(const std::string& str, pattern& arg) { + arg.pat = str; + init_bad_char(str, arg.bad_char); + init_good_suffix(str, arg.good_suffix); +} +/** + * @brief A function that implements Boyer-Moore's algorithm. + * + * @param str Text we are seatching in. + * @param arg pattern structure containing the preprocessed pattern + * @return Vector of indexes of the occurrences of pattern in text + */ +std::vector search(const std::string& str, const pattern& arg) { + size_t index_position = arg.pat.size() - 1; + std::vector index_storage; + + while (index_position < str.length()) { + size_t index_string = index_position; + int index_pattern = static_cast(arg.pat.size()) - 1; + + while (index_pattern >= 0 && + str[index_string] == arg.pat[index_pattern]) { + --index_pattern; + --index_string; + } + + if (index_pattern < 0) { + index_storage.push_back(index_position - arg.pat.length() + 1); + index_position += arg.good_suffix[0]; + } else { + index_position += std::max(arg.bad_char[str[index_string]], + arg.good_suffix[index_pattern + 1]); + } + } + + return index_storage; +} + +/** + * @brief Check if pat is prefix of str. + * + * @param str pointer to some part of the input text. + * @param pat the searched pattern. + * @param len length of the searched pattern + * @returns `true` if pat IS prefix of str. + * @returns `false` if pat is NOT a prefix of str. + */ +bool is_prefix(const char* str, const char* pat, size_t len) { + if (strlen(str) < len) { + return false; + } + + for (size_t i = 0; i < len; i++) { + if (str[i] != pat[i]) { + return false; + } + } + + return true; +} +} // namespace boyer_moore +} // namespace strings +/** + * @brief A test case in which we search for every appearance of the word 'and' + * @param text The text in which we search for appearance of the word 'and' + * @returns void + */ +void and_test(const char* text) { + strings::boyer_moore::pattern ands; + strings::boyer_moore::init_pattern("and", ands); + std::vector indexes = strings::boyer_moore::search(text, ands); + + assert(indexes.size() == 2); + assert(strings::boyer_moore::is_prefix(text + indexes[0], "and", 3)); + assert(strings::boyer_moore::is_prefix(text + indexes[1], "and", 3)); +} + +/** + * @brief A test case in which we search for every appearance of the word 'pat' + * @param text The text in which we search for appearance of the word 'pat' + * @returns void + */ +void pat_test(const char* text) { + strings::boyer_moore::pattern pat; + strings::boyer_moore::init_pattern("pat", pat); + std::vector indexes = strings::boyer_moore::search(text, pat); + + assert(indexes.size() == 6); + + for (const auto& currentIndex : indexes) { + assert(strings::boyer_moore::is_prefix(text + currentIndex, "pat", 3)); + } +} +/** + * @brief Self-test implementations + * @returns void + */ +static void tests() { + const char* text = + "When pat Mr. and Mrs. pat Dursley woke up on the dull, gray \ + Tuesday our story starts, \ + there was nothing about pat the cloudy sky outside to pat suggest that\ + strange and \ + mysterious things would pat soon be happening all pat over the \ + country."; + + and_test(text); + pat_test(text); + + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + tests(); // run self-test implementations + return 0; +}