mirror of
https://hub.njuu.cf/TheAlgorithms/C-Plus-Plus.git
synced 2023-10-11 13:05:55 +08:00
Merge branch 'master' into check_amicable_pair
This commit is contained in:
commit
e862c87c2f
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
||||
* @Panquesito7 @realstealthninja
|
23
.github/workflows/awesome_workflow.yml
vendored
23
.github/workflows/awesome_workflow.yml
vendored
@ -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
|
||||
@ -94,6 +89,8 @@ jobs:
|
||||
name: Compile checks
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [MainSequence]
|
||||
permissions:
|
||||
pull-requests: write
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
@ -101,5 +98,17 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- run: cmake -B ./build -S .
|
||||
- run: cmake --build build
|
||||
- run: |
|
||||
cmake -B ./build -S .
|
||||
cmake --build build
|
||||
- name: Label on PR fail
|
||||
uses: actions/github-script@v6
|
||||
if: ${{ failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' }}
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['automated tests are failing']
|
||||
})
|
||||
|
31
.github/workflows/directory_writer.yml
vendored
Normal file
31
.github/workflows/directory_writer.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: Directory writer
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
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 * * 1'
|
||||
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/
|
29
.vscode/settings.json
vendored
29
.vscode/settings.json
vendored
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
cmake_minimum_required(VERSION 3.26.4)
|
||||
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)
|
||||
|
@ -120,6 +120,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 +171,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 +293,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)
|
||||
|
@ -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.
|
||||
|
16
cpu_scheduling_algorithms/CMakeLists.txt
Normal file
16
cpu_scheduling_algorithms/CMakeLists.txt
Normal file
@ -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} )
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Program to find the Longest Palindormic
|
||||
* Subsequence of a string
|
||||
* @brief Program to find the [Longest Palindormic
|
||||
* Subsequence](https://www.geeksforgeeks.org/longest-palindromic-subsequence-dp-12/) of a string
|
||||
*
|
||||
* @details
|
||||
* [Palindrome](https://en.wikipedia.org/wiki/Palindrome) string sequence of
|
||||
@ -13,26 +13,33 @@
|
||||
* @author [Anjali Jha](https://github.com/anjali1903)
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cassert> /// for assert
|
||||
#include <string> /// for std::string
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/**
|
||||
* Function that returns the longest palindromic
|
||||
* subsequence of a string
|
||||
* @namespace
|
||||
* @brief Dynamic Programming algorithms
|
||||
*/
|
||||
std::string lps(std::string a) {
|
||||
std::string b = a;
|
||||
reverse(b.begin(), b.end());
|
||||
int m = a.length();
|
||||
std::vector<std::vector<int> > res(m + 1);
|
||||
namespace dynamic_programming {
|
||||
/**
|
||||
* @brief Function that returns the longest palindromic
|
||||
* subsequence of a string
|
||||
* @param a string whose longest palindromic subsequence is to be found
|
||||
* @returns longest palindromic subsequence of the string
|
||||
*/
|
||||
std::string lps(const std::string& a) {
|
||||
const auto b = std::string(a.rbegin(), a.rend());
|
||||
const auto m = a.length();
|
||||
using ind_type = std::string::size_type;
|
||||
std::vector<std::vector<ind_type> > res(m + 1,
|
||||
std::vector<ind_type>(m + 1));
|
||||
|
||||
// Finding the length of the longest
|
||||
// palindromic subsequence and storing
|
||||
// in a 2D array in bottoms-up manner
|
||||
for (int i = 0; i <= m; i++) {
|
||||
for (int j = 0; j <= m; j++) {
|
||||
for (ind_type i = 0; i <= m; i++) {
|
||||
for (ind_type j = 0; j <= m; j++) {
|
||||
if (i == 0 || j == 0) {
|
||||
res[i][j] = 0;
|
||||
} else if (a[i - 1] == b[j - 1]) {
|
||||
@ -43,10 +50,10 @@ std::string lps(std::string a) {
|
||||
}
|
||||
}
|
||||
// Length of longest palindromic subsequence
|
||||
int idx = res[m][m];
|
||||
auto idx = res[m][m];
|
||||
// Creating string of index+1 length
|
||||
std::string ans(idx + 1, '\0');
|
||||
int i = m, j = m;
|
||||
std::string ans(idx, '\0');
|
||||
ind_type i = m, j = m;
|
||||
|
||||
// starting from right-most bottom-most corner
|
||||
// and storing them one by one in ans
|
||||
@ -70,19 +77,22 @@ std::string lps(std::string a) {
|
||||
|
||||
return ans;
|
||||
}
|
||||
} // namespace dynamic_programming
|
||||
|
||||
/** Test function */
|
||||
void test() {
|
||||
// lps("radar") return "radar"
|
||||
assert(lps("radar") == "radar");
|
||||
// lps("abbcbaa") return "abcba"
|
||||
assert(lps("abbcbaa") == "abcba");
|
||||
// lps("bbbab") return "bbbb"
|
||||
assert(lps("bbbab") == "bbbb");
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
assert(dynamic_programming::lps("radar") == "radar");
|
||||
assert(dynamic_programming::lps("abbcbaa") == "abcba");
|
||||
assert(dynamic_programming::lps("bbbab") == "bbbb");
|
||||
assert(dynamic_programming::lps("") == "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Function
|
||||
* @brief Main Function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // execute the tests
|
||||
|
16
games/CMakeLists.txt
Normal file
16
games/CMakeLists.txt
Normal file
@ -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} )
|
416
games/memory_game.cpp
Normal file
416
games/memory_game.cpp
Normal file
@ -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 <algorithm> /// for std::shuffle()
|
||||
#include <cstdlib> /// for std::srand()
|
||||
#include <ctime> /// for std::time()
|
||||
#include <iostream> /// for IO operations
|
||||
#include <random> /// for std::mt19937
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
// `Sleep` is only available in Windows in milliseconds.
|
||||
// However, on Unix/Linux systems it is `sleep`, in seconds.
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h> /// for Sleep()
|
||||
template <typename T>
|
||||
constexpr typename std::enable_if<std::is_integral<T>::value, void>::type SLEEP(
|
||||
T milliseconds) {
|
||||
Sleep(milliseconds * 1000);
|
||||
}
|
||||
#else
|
||||
#include <unistd.h> /// for sleep()
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
void init(std::vector<T> *table) {
|
||||
std::vector<char> 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<char> 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 <typename T>
|
||||
void print_table(const std::vector<T> &table) {
|
||||
std::cout << "| ";
|
||||
std::vector<T> 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 <typename T>
|
||||
void reset_data(const std::vector<T> &, 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 <typename T>
|
||||
void ask_data(const std::vector<T> &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 <typename T>
|
||||
void reset_data(const std::vector<T> &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 <typename T>
|
||||
bool match(const std::vector<T> &table, std::vector<T> *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 <typename T>
|
||||
void assign_results(std::vector<T> *table_empty, std::vector<T> *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<void>::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<char> table(size);
|
||||
std::vector<char> 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;
|
||||
}
|
329
hashing/sha256.cpp
Normal file
329
hashing/sha256.cpp
Normal file
@ -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 <array> /// For std::array
|
||||
#include <cassert> /// For assert
|
||||
#include <cstdint> /// For uint8_t, uint32_t and uint64_t data types
|
||||
#include <iomanip> /// For std::setfill and std::setw
|
||||
#include <iostream> /// For IO operations
|
||||
#include <sstream> /// For std::stringstream
|
||||
#include <utility> /// For std::move
|
||||
#include <vector> /// 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<uint32_t, 8> hash = {0x6A09E667, 0xBB67AE85, 0x3C6EF372,
|
||||
0xA54FF53A, 0x510E527F, 0x9B05688C,
|
||||
0x1F83D9AB, 0x5BE0CD19};
|
||||
|
||||
public:
|
||||
void update(const std::array<uint32_t, 64> &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<uint32_t, 64> &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<uint32_t, 64> 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 <typename T>
|
||||
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<char>(
|
||||
extract_byte<size_t>(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<uint32_t, 64> Message schedule array
|
||||
*/
|
||||
std::array<uint32_t, 64> create_message_schedule_array(const std::string &input,
|
||||
const size_t byte_num) {
|
||||
std::array<uint32_t, 64> 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<uint8_t>(get_char(input, byte_num + block_num * 4))
|
||||
<< 24) |
|
||||
(static_cast<uint8_t>(get_char(input, byte_num + block_num * 4 + 1))
|
||||
<< 16) |
|
||||
(static_cast<uint8_t>(get_char(input, byte_num + block_num * 4 + 2))
|
||||
<< 8) |
|
||||
static_cast<uint8_t>(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<uint32_t>(512, 0) == 0);
|
||||
assert(hashing::sha256::extract_byte<uint32_t>(512, 1) == 2);
|
||||
bool exception = false;
|
||||
try {
|
||||
hashing::sha256::extract_byte<uint32_t>(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<TestCase> 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;
|
||||
}
|
@ -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 <cassert>
|
||||
#include <iostream>
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for cout
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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 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;
|
||||
}
|
||||
n = n / i;
|
||||
}
|
||||
if (n == 1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Test function
|
||||
/*!
|
||||
* 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 was the sum of a factorial then it should be divided until it
|
||||
* becomes 1
|
||||
*/
|
||||
return (n == 1);
|
||||
}
|
||||
} // namespace math
|
||||
|
||||
/**
|
||||
* @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";
|
||||
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 2:\t n=720\n";
|
||||
assert(is_factorial(720) == true);
|
||||
std::cout << "passed\n";
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 <cassert> /// for assert
|
||||
#include <iostream> /// for IO operations
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @brief Mathematical algorithms
|
||||
* @namespace
|
||||
*/
|
||||
template <typename T>
|
||||
bool is_prime(T num) {
|
||||
bool result = true;
|
||||
namespace math {
|
||||
/**
|
||||
* @brief Function to check if the given number is prime or not.
|
||||
* @param num number to be checked.
|
||||
* @return true if number is a prime
|
||||
* @return false if number is not a prime.
|
||||
*/
|
||||
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
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
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);
|
||||
|
||||
std::cout << "All tests have successfully passed!" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
// perform self-test
|
||||
assert(is_prime(50) == false);
|
||||
assert(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;
|
||||
}
|
||||
|
||||
tests(); // perform self-tests implementations
|
||||
return 0;
|
||||
}
|
||||
|
@ -15,10 +15,9 @@
|
||||
* @author [Swastika Gupta](https://github.com/Swastyy)
|
||||
*/
|
||||
|
||||
#include <algorithm> /// for std::is_equal, std::swap
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for IO operations
|
||||
#include <vector> /// for std::vector
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for std::cout
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/**
|
||||
* @namespace math
|
||||
@ -39,10 +38,17 @@ namespace n_bonacci {
|
||||
* @returns the n-bonacci sequence as vector array
|
||||
*/
|
||||
std::vector<uint64_t> N_bonacci(const uint64_t &n, const uint64_t &m) {
|
||||
std::vector<uint64_t> a(m, 0); // we create an empty array of size m
|
||||
std::vector<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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;
|
||||
struct TestCase {
|
||||
const uint64_t n;
|
||||
const uint64_t m;
|
||||
const std::vector<uint64_t> expected;
|
||||
TestCase(const uint64_t in_n, const uint64_t in_m,
|
||||
std::initializer_list<uint64_t> data)
|
||||
: n(in_n), m(in_m), expected(data) {
|
||||
assert(data.size() == m);
|
||||
}
|
||||
};
|
||||
const std::vector<TestCase> 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 = 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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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;
|
||||
|
||||
// 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<uint64_t> 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<uint64_t> 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;
|
||||
}
|
||||
|
||||
|
81
others/kelvin_to_celsius.cpp
Normal file
81
others/kelvin_to_celsius.cpp
Normal file
@ -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 <cassert> /// for assert
|
||||
#include <cmath> /// for std::abs
|
||||
#include <iostream> /// 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;
|
||||
}
|
16
physics/CMakeLists.txt
Normal file
16
physics/CMakeLists.txt
Normal file
@ -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} )
|
272
strings/boyer_moore.cpp
Normal file
272
strings/boyer_moore.cpp
Normal file
@ -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 <cassert> /// for assert
|
||||
#include <climits> /// for CHAR_MAX macro
|
||||
#include <cstring> /// for strlen
|
||||
#include <iostream> /// for IO operations
|
||||
#include <string> /// for std::string
|
||||
#include <vector> /// 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<size_t>
|
||||
bad_char; ///< bad char table used in [Bad Character
|
||||
///< Heuristic](https://www.geeksforgeeks.org/boyer-moore-algorithm-for-pattern-searching/)
|
||||
|
||||
std::vector<size_t>
|
||||
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<size_t>& 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<size_t> 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<size_t>& 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<size_t> search(const std::string& str, const pattern& arg) {
|
||||
size_t index_position = arg.pat.size() - 1;
|
||||
std::vector<size_t> index_storage;
|
||||
|
||||
while (index_position < str.length()) {
|
||||
size_t index_string = index_position;
|
||||
int index_pattern = static_cast<int>(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<size_t> 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<size_t> 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user