Merge remote-tracking branch 'upstream/master' into quick_sort

This commit is contained in:
Krishna Vedala
2020-07-18 17:31:02 -04:00
58 changed files with 4161 additions and 709 deletions

6
.clang-tidy Normal file
View File

@@ -0,0 +1,6 @@
---
Checks: '-*,google-*,clang-analyzer-*,-clang-analyzer-security.insecureAPI.*,cppcoreguidelines-*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-bounds-*,openmp-*,performance-*,portability-*,modernize-*,-modernize-use-trailing-*'
WarningsAsErrors: '*,-google-readability-*,-google-explicit-constructor,-modernize-*,modernize-avoid-c-arrays,-performance-move-const-arg,-performance-noexcept-move-constructor,-cppcoreguidelines-init-variables,-cppcoreguidelines-pro-*,-cppcoreguidelines-owning-memory,-clang-analyzer-cplusplus.Move'
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: '{ BasedOnStyle: Google, UseTab: Never, IndentWidth: 4, TabWidth: 4, AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: true, ColumnLimit: 80, AccessModifierOffset: -3, AlignConsecutiveMacros: true }'

View File

@@ -16,7 +16,9 @@ jobs:
- name: requirements
run: |
sudo apt -qq -y update
sudo apt -qq install clang-format
sudo apt -qq install clang-tidy-10
# checks are passing with less errors when used with this version.
# The default installs v6.0 which did not work out well in my tests
- name: Setup Git Specs
run: |
git config --global user.name github-actions
@@ -43,18 +45,6 @@ jobs:
fi
done
git commit -am "formatting filenames $GITHUB_SHA" || true
- name: Clang Formatter
run: |
for fname in $(find . -name '*.cpp' -o -name '*.h')
do
clang-format --verbose -i --style="$line1 $line2 $line3 $line4" "$fname"
done
git commit -am "formatting source-code for $GITHUB_SHA" || true
env:
line1: "{ BasedOnStyle: Google, UseTab: Never,"
line2: "IndentWidth: 4, TabWidth: 4, "
line3: "AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: false,"
line4: "ColumnLimit: 80, AccessModifierOffset: -3 }"
- name: Update DIRECTORY.md
shell: python
@@ -100,24 +90,21 @@ jobs:
with open("DIRECTORY.md", "w") as out_file:
out_file.write(build_directory_md(".") + "\n")
- name: Update DIRECTORY.md
- name: Commit DIRECTORY.md
run: git commit -m "updating DIRECTORY.md" DIRECTORY.md || true
- name: Get file changes
run: |
cat DIRECTORY.md
git config --global user.name github-actions
git config --global user.email '${GITHUB_ACTOR}@users.noreply.github.com'
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
git add DIRECTORY.md
git commit -am "updating DIRECTORY.md" || true
git push --force origin HEAD:$GITHUB_REF || true
- name: Install CPPLINT
run: |
python -m pip install cpplint
git remote -v
git branch
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
git diff --diff-filter=dr --name-only origin/master > git_diff.txt
echo "Files changed-- `cat git_diff.txt`"
- name: cpplint_modified_files
- name: Configure for static lint checks
# compiling first gives clang-tidy access to all the header files and settings used to compile the programs.
# This will check for macros, if any, on linux and not for Windows. But the use of portability checks should
# 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
@@ -135,9 +122,11 @@ jobs:
if not cpp_files:
sys.exit(0)
print("cpplint:")
for cpp_file in cpp_files:
subprocess.run(["cpplint", "--filter=-legal/copyright,-build/include", cpp_file], check=True, text=True)
subprocess.run(["clang-tidy-10", "--fix", "-p=build", "--extra-arg=-std=c++11", *cpp_files, "--"],
check=True, text=True, stderr=subprocess.STDOUT)
# for cpp_file in cpp_files:
# subprocess.run(["clang-tidy-10", "--fix", "-p=build", cpp_file, "--"],
# check=True, text=True, stderr=subprocess.STDOUT)
# print("g++:")
# compile_exts = tuple(".c .c++ .cc .cpp .cu .cxx".split())
@@ -163,6 +152,11 @@ jobs:
bad_files = len(upper_files + space_files + nodir_files)
if bad_files:
sys.exit(bad_files)
- name: Commit and push changes
run: |
git diff DIRECTORY.md
git commit -am "clang-tidy fixes for $GITHUB_SHA" || true
git push --force origin HEAD:$GITHUB_REF || true
build:
name: Compile checks

View File

@@ -1,9 +1,9 @@
FROM gitpod/workspace-full
FROM gitpod/workspace-full-vnc
RUN sudo apt-get update \
&& sudo apt-get install -y \
doxygen \
graphviz \
ninja-build \
&& pip install cpplint \
&& sudo rm -rf /var/lib/apt/lists/*
&& sudo apt-get install -y \
doxygen \
graphviz \
ninja-build \
&& pip install cpplint \
&& sudo rm -rf /var/lib/apt/lists/*

View File

@@ -7,6 +7,7 @@ github:
addComment: false
addCheck: false
master: true
branches: true
pullRequestsFromForks: true
vscode:

View File

@@ -1,5 +1,5 @@
{
"C_Cpp.clang_format_style": "{ BasedOnStyle: Google, UseTab: Never, IndentWidth: 4, TabWidth: 4, AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: true, ColumnLimit: 80, AccessModifierOffset: -3 }",
"C_Cpp.clang_format_style": "{ BasedOnStyle: Google, UseTab: Never, IndentWidth: 4, TabWidth: 4, AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: true, ColumnLimit: 80, AccessModifierOffset: -3, AlignConsecutiveMacros: true }",
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.formatOnPaste": true

View File

@@ -17,7 +17,28 @@ if(MSVC)
endif(MSVC)
option(USE_OPENMP "flag to use OpenMP for multithreading" ON)
if(USE_OPENMP)
find_package(OpenMP)
if (OpenMP_CXX_FOUND)
message(STATUS "Building with OpenMP Multithreading.")
else()
message(STATUS "No OpenMP found, no multithreading.")
endif()
endif()
add_subdirectory(math)
add_subdirectory(others)
add_subdirectory(search)
add_subdirectory(ciphers)
add_subdirectory(strings)
add_subdirectory(sorting)
add_subdirectory(geometry)
add_subdirectory(graphics)
add_subdirectory(probability)
add_subdirectory(data_structures)
add_subdirectory(machine_learning)
add_subdirectory(numerical_methods)
cmake_policy(SET CMP0054 NEW)
cmake_policy(SET CMP0057 NEW)
find_package(Doxygen OPTIONAL_COMPONENTS dot dia)
@@ -25,7 +46,7 @@ if(DOXYGEN_FOUND)
set(DOXYGEN_GENERATE_MAN NO)
set(DOXYGEN_USE_MATHJAX YES)
set(DOXYGEN_GENERATE_HTML YES)
set(DOXYGEN_HTML_TIMESTAMP YES)
# set(DOXYGEN_HTML_TIMESTAMP YES)
set(DOXYGEN_EXTRACT_STATIC YES)
set(DOXYGEN_INLINE_SOURCES YES)
set(DOXYGEN_CREATE_SUBDIRS YES)
@@ -34,6 +55,7 @@ if(DOXYGEN_FOUND)
set(DOXYGEN_STRIP_CODE_COMMENTS NO)
set(DOXYGEN_EXT_LINKS_IN_WINDOW YES)
set(DOXYGEN_BUILTIN_STL_SUPPORT YES)
set(DOXYGEN_ENABLE_PREPROCESSING YES)
set(DOXYGEN_CLANG_ASSISTED_PARSING YES)
set(DOXYGEN_FILE_PATTERNS *.cpp *.h *.hpp *.md)
set(DOXYGEN_MATHJAX_EXTENSIONS TeX/AMSmath TeX/AMSsymbols)
@@ -48,6 +70,12 @@ if(DOXYGEN_FOUND)
set(DOXYGEN_INTERACTIVE_SVG YES)
set(DOXYGEN_DOT_IMAGE_FORMAT "svg")
endif()
if(OPENMP_FOUND)
set(DOXYGEN_PREDEFINED "_OPENMP=1")
endif()
if(GLUT_FOUND)
set(DOXYGEN_PREDEFINED ${DOXYGEN_PREDEFINED} "GLUT_FOUND=1")
endif()
doxygen_add_docs(
doc
@@ -56,26 +84,6 @@ if(DOXYGEN_FOUND)
)
endif()
if(USE_OPENMP)
find_package(OpenMP)
if (OpenMP_CXX_FOUND)
message(STATUS "Building with OpenMP Multithreading.")
else()
message(STATUS "No OpenMP found, no multithreading.")
endif()
endif()
add_subdirectory(math)
add_subdirectory(others)
add_subdirectory(search)
add_subdirectory(strings)
add_subdirectory(sorting)
add_subdirectory(geometry)
add_subdirectory(probability)
add_subdirectory(data_structures)
add_subdirectory(machine_learning)
add_subdirectory(numerical_methods)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

View File

@@ -28,8 +28,85 @@ We are very happy that you consider implementing algorithms and data structures
- Strictly use snake_case (underscore_separated) in filenames.
- If you have added or modified code, please make sure the code compiles before submitting.
- Our automated testing runs [__cpplint__](https://github.com/cpplint/cpplint) on all pull requests so please be sure that your code passes before submitting.
- Please conform to [doxygen](https://www.doxygen.nl/manual/docblocks.html) standard and document the code as much as possible. This not only facilitates the readers but also generates the correct info on website.
- **Be consistent in use of these guidelines.**
#### Documentation
- Make sure you put useful comments in your code. Do not comment things that are obvious.
- Please avoid creating new directories if at all possible. Try to fit your work into the existing directory structure. If you want to create a new directory, then please check if a similar category has been recently suggested or created by other pull requests.
- If you have modified/added documentation, please ensure that your language is concise and contains no grammar errors.
- Do not update README.md along with other changes, first create an issue and then link to that issue in your pull request to suggest specific changes required to README.md
- The repository follows [Doxygen](https://www.doxygen.nl/manual/docblocks.html) standards and auto-generates the [repo website](https://thealgorithms.github.io/C-Plus-Plus). Please ensure the code is documented in this structure. Sample implementation is given below.
#### Test
- Make sure to add examples and test cases in your main() function.
- If you find any algorithm or document without tests, please feel free to create a pull request or issue describing suggested changes.
- Please try to add one or more `test()` functions that will invoke the algorithm implementation on random test data with expected output. Use `assert()` function to confirm that the tests will pass.
#### Typical structure of a program:
```cpp
/**
* @file
* @brief Add one line description here
* @details
* This is a multi line
* description containing links, references,
* math equations, etc
* @author [Name](https://github.com/handle)
* @see related_file.cpp, another_file.cpp
*/
#include
/**
* @namespace <check from other files in this repo>
*/
namespace name {
/**
* Class documentation
*/
class cls_name{
private:
int var1; ///< short info of this variable
char *msg; ///< short info
public:
// other members also documented as below
}
/**
* Function documentation
* @tparam T this is a one-line info about T
* @param param1 on-line info about param1
* @param param2 on-line info about param2
* @returns `true` if ...
* @returns `false` if ...
*/
template<class T>
bool func(int param1, T param2) {
// function statements here
if(/*something bad*/)
return false;
return true;
}
/** Test function */
void test() {
/* some statements */
assert(func(...) == ...); // this ensures that the algorithm works as expected
// can have multiple checks
}
/** Main function */
int main(int argc, char *argv[]) {
// code here
return 0;
}
```
#### New File Name guidelines
- Use lowercase words with ``"_"`` as separator
- For instance
@@ -70,16 +147,6 @@ Common prefixes:
- docs: Documentation changes
- test: Correct existing tests or add new ones
#### Documentation
- Make sure you put useful comments in your code. Do not comment things that are obvious.
- Please avoid creating new directories if at all possible. Try to fit your work into the existing directory structure. If you want to create a new directory, then please check if a similar category has been recently suggested or created by other pull requests.
- If you have modified/added documentation, please ensure that your language is concise and contains no grammar errors.
- Do not update README.md along with other changes, first create an issue and then link to that issue in your pull request to suggest specific changes required to README.md
#### Test
- Make sure to add examples and test cases in your main() function.
- If you find any algorithm or document without tests, please feel free to create a pull request or issue describing suggested changes.
### Pull Requests
- Checkout our [pull request template](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/.github/pull_request_template.md)

View File

@@ -10,6 +10,9 @@
* [Rat Maze](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/backtracking/rat_maze.cpp)
* [Sudoku Solve](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/backtracking/sudoku_solve.cpp)
## Ciphers
* [Hill Cipher](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/ciphers/hill_cipher.cpp)
## Data Structures
* [Avltree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/avltree.cpp)
* [Binary Search Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/binary_search_tree.cpp)
@@ -30,6 +33,8 @@
* [Queue Using Array2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/queue_using_array2.cpp)
* [Queue Using Linked List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/queue_using_linked_list.cpp)
* [Queue Using Linkedlist](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/queue_using_linkedlist.cpp)
* [Queue Using Two Stacks](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/queue_using_two_stacks.cpp)
* [Skip List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/skip_list.cpp)
* [Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/stack.h)
* [Stack Using Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/stack_using_array.cpp)
* [Stack Using Linked List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/stack_using_linked_list.cpp)
@@ -81,6 +86,9 @@
* [Topological Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/topological_sort.cpp)
* [Topological Sort By Kahns Algo](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/topological_sort_by_kahns_algo.cpp)
## Graphics
* [Spirograph](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graphics/spirograph.cpp)
## Greedy Algorithms
* [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/dijkstra.cpp)
* [Huffman](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/huffman.cpp)
@@ -98,10 +106,14 @@
* [Adaline Learning](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/adaline_learning.cpp)
* [Kohonen Som Topology](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/kohonen_som_topology.cpp)
* [Kohonen Som Trace](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/kohonen_som_trace.cpp)
* [Ordinary Least Squares Regressor](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/ordinary_least_squares_regressor.cpp)
## Math
* [Armstrong Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/armstrong_number.cpp)
* [Binary Exponent](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/binary_exponent.cpp)
* [Check Amicable Pair](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/check_amicable_pair.cpp)
* [Check Prime](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/check_prime.cpp)
* [Complex Numbers](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/complex_numbers.cpp)
* [Double Factorial](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/double_factorial.cpp)
* [Eulers Totient Function](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/eulers_totient_function.cpp)
* [Extended Euclid Algorithm](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/extended_euclid_algorithm.cpp)
@@ -116,6 +128,7 @@
* [Large Factorial](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/large_factorial.cpp)
* [Large Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/large_number.h)
* [Least Common Multiple](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/least_common_multiple.cpp)
* [Miller Rabin](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/miller_rabin.cpp)
* [Modular Inverse Fermat Little Theorem](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_inverse_fermat_little_theorem.cpp)
* [Number Of Positive Divisors](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/number_of_positive_divisors.cpp)
* [Power For Huge Numbers](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/power_for_huge_numbers.cpp)
@@ -126,18 +139,21 @@
* [Sieve Of Eratosthenes](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/sieve_of_eratosthenes.cpp)
* [Sqrt Double](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/sqrt_double.cpp)
* [String Fibonacci](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/string_fibonacci.cpp)
* [Sum Of Digits](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/sum_of_digits.cpp)
## Numerical Methods
* [Bisection Method](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/bisection_method.cpp)
* [Brent Method Extrema](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/brent_method_extrema.cpp)
* [Durand Kerner Roots](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/durand_kerner_roots.cpp)
* [False Position](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/false_position.cpp)
* [Gaussian Elimination](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/gaussian_elimination.cpp)
* [Golden Search Extrema](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/golden_search_extrema.cpp)
* [Lu Decompose](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/lu_decompose.cpp)
* [Lu Decomposition](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/lu_decomposition.h)
* [Newton Raphson Method](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/newton_raphson_method.cpp)
* [Ode Forward Euler](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/ode_forward_euler.cpp)
* [Ode Midpoint Euler](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/ode_midpoint_euler.cpp)
* [Ode Semi Implicit Euler](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/ode_semi_implicit_euler.cpp)
* [Ordinary Least Squares Regressor](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/ordinary_least_squares_regressor.cpp)
* [Qr Decompose](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/qr_decompose.h)
* [Qr Decomposition](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/qr_decomposition.cpp)
* [Qr Eigen Values](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/qr_eigen_values.cpp)
@@ -180,14 +196,14 @@
* [Poisson Dist](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/probability/poisson_dist.cpp)
## Range Queries
* [Bit](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/bit.cpp)
* [Fenwicktree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/fenwicktree.cpp)
* [Fenwick Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/fenwick_tree.cpp)
* [Mo](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/mo.cpp)
* [Segtree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/segtree.cpp)
## Search
* [Binary Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/binary_search.cpp)
* [Exponential Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/exponential_search.cpp)
* [Fibonacci Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/fibonacci_search.cpp)
* [Hash Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/hash_search.cpp)
* [Interpolation Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/interpolation_search.cpp)
* [Interpolation Search2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/interpolation_search2.cpp)
@@ -200,12 +216,14 @@
## Sorting
* [Bead Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/bead_sort.cpp)
* [Bitonic Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/bitonic_sort.cpp)
* [Bogo Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/bogo_sort.cpp)
* [Bubble Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/bubble_sort.cpp)
* [Bucket Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/bucket_sort.cpp)
* [Cocktail Selection Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/cocktail_selection_sort.cpp)
* [Comb Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/comb_sort.cpp)
* [Counting Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/counting_sort.cpp)
* [Counting Sort String](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/counting_sort_string.cpp)
* [Gnome Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/gnome_sort.cpp)
* [Heap Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/heap_sort.cpp)
* [Insertion Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/insertion_sort.cpp)
* [Library Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/library_sort.cpp)

View File

@@ -1,18 +1,36 @@
# The Algorithms - C++ # {#mainpage}
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/TheAlgorithms/C-Plus-Plus)
<!-- the suffix in the above line is required for doxygen to consider this as the index page of the generated documentation site -->
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/TheAlgorithms/C-Plus-Plus)
[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/TheAlgorithms/C-Plus-Plus.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/TheAlgorithms/C-Plus-Plus/context:cpp)
[![Gitter chat](https://img.shields.io/badge/Chat-Gitter-ff69b4.svg?label=Chat&logo=gitter&style=flat-square)](https://gitter.im/TheAlgorithms)
<a href="https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/CONTRIBUTING.md">![contributions welcome](https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3&style=flat-square)</a>
[![contributions welcome](https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3&style=flat-square)](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/CONTRIBUTING.md)
![GitHub repo size](https://img.shields.io/github/repo-size/TheAlgorithms/C-Plus-Plus?color=red&style=flat-square)
![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed/TheAlgorithms/C-Plus-Plus?color=green&style=flat-square)
<a href="https://TheAlgorithms.github.io/C-Plus-Plus">![Doxygen CI](https://github.com/TheAlgorithms/C-Plus-Plus/workflows/Doxygen%20CI/badge.svg)</a>
<a href="https://github.com/TheAlgorithms/C-Plus-Plus/actions?query=workflow%3A%22Awesome+CI+Workflow%22">![Awesome CI](https://github.com/TheAlgorithms/C-Plus-Plus/workflows/Awesome%20CI%20Workflow/badge.svg)</a>
[![Doxygen CI](https://github.com/TheAlgorithms/C-Plus-Plus/workflows/Doxygen%20CI/badge.svg)](https://TheAlgorithms.github.io/C-Plus-Plus)
[![Awesome CI](https://github.com/TheAlgorithms/C-Plus-Plus/workflows/Awesome%20CI%20Workflow/badge.svg)](https://github.com/TheAlgorithms/C-Plus-Plus/actions?query=workflow%3A%22Awesome+CI+Workflow%22)
[Online Documentation](https://TheAlgorithms.github.io/C-Plus-Plus).
## Overview
The repository is a collection of open-source implementation of a variety of algorithms implemented in C++ and licensed under [MIT License](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/LICENSE). The algorithms span a variety of topics from computer science, mathematics and statistics, data science, machine learning, engineering, etc.. The implementations and the associated documentation are meant to provide a learning resource for educators and students. Hence, one may find more than one implementation for the same objective but using a different algorithm strategies and optimizations.
## Features
* 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.
* 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.
## Documentation
[Online Documentation](https://TheAlgorithms.github.io/C-Plus-Plus) is generated from the repository source codes directly. The documentation contains all resources including source code snippets, details on execution of the programs, diagrammatic representation of program flow, and links to external resources where necessary. The documentation also introduces interactive source code with links to documentation for C++ STL library functions used.
Click on [Files menu](https://TheAlgorithms.github.io/C-Plus-Plus/files.html) to see the list of all the files documented with the code.
### Algorithms implemented in C++ (for education)
The implementations are for learning purpose. They may be less efficient than the implementation in the standard library.
[Documentation of Algorithms in C++](https://thealgorithms.github.io/C-Plus-Plus) by [The Algorithms Contributors](https://github.com/TheAlgorithms/C-Plus-Plus/graphs/contributors) is licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/?ref=chooser-v1)<br/>
<a href="https://creativecommons.org/licenses/by-sa/4.0"><img alt="Creative Commons License" style="height:22px!important;margin-left: 3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg" /><img alt="Credit must be given to the creator" style="height:22px!important;margin-left: 3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg" /><img alt="Adaptations must be shared under the same terms" style="height:22px!important;margin-left: 3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/sa.svg" /></a>
### Contribute Guidelines
Read our [Contribution Guidelines](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/CONTRIBUTING.md) before you contribute.
## Contributions
As a community developed and maintained repository, we welcome new un-plagiarized quality contributions. Please read our [Contribution Guidelines](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/CONTRIBUTING.md).

18
ciphers/CMakeLists.txt Normal file
View File

@@ -0,0 +1,18 @@
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
# with full pathname. RELATIVE may makes it easier to extract an executable name
# automatically.
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
foreach( testsourcefile ${APP_SOURCES} )
# I used a simple string replace, to cut off .cpp.
string( REPLACE ".cpp" "" testname ${testsourcefile} )
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/ciphers")
endforeach( testsourcefile ${APP_SOURCES} )

543
ciphers/hill_cipher.cpp Normal file
View File

@@ -0,0 +1,543 @@
/**
* @file hill_cipher.cpp
* @brief Implementation of [Hill
* cipher](https://en.wikipedia.org/wiki/Hill_cipher) algorithm.
*
* Program to generate the encryption-decryption key and perform encryption and
* decryption of ASCII text using the famous block cipher algorithm. This is a
* powerful encryption algorithm that is relatively easy to implement with a
* given key. The strength of the algorithm depends on the size of the block
* encryption matrix key; the bigger the matrix, the stronger the encryption and
* more difficult to break it. However, the important requirement for the matrix
* is that:
* 1. matrix should be invertible - all inversion conditions should be satisfied
* and
* 2. its determinant must not have any common factors with the length of
* character set
* Due to this restriction, most implementations only implement with small 3x3
* encryption keys and a small subset of ASCII alphabets.
*
* In the current implementation, I present to you an implementation for
* generating larger encryption keys (I have attempted upto 10x10) and an ASCII
* character set of 97 printable characters. Hence, a typical ASCII text file
* could be easily encrypted with the module. The larger character set increases
* the modulo of cipher and hence the matrix determinants can get very large
* very quickly rendering them ill-defined.
*
* \note This program uses determinant computation using LU decomposition from
* the file lu_decomposition.h
* \note The matrix generation algorithm is very rudimentary and does not
* guarantee an invertible modulus matrix. \todo Better matrix generation
* algorithm.
*
* @author [Krishna Vedala](https://github.com/kvedala)
*/
#include <cassert>
#include <cmath>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#ifdef _OPENMP
#include <omp.h>
#endif
#include "../numerical_methods/lu_decomposition.h"
/**
* operator to print a matrix
*/
template <typename T>
static std::ostream &operator<<(std::ostream &out, matrix<T> const &v) {
const int width = 15;
const char separator = ' ';
for (size_t row = 0; row < v.size(); row++) {
for (size_t col = 0; col < v[row].size(); col++)
out << std::left << std::setw(width) << std::setfill(separator)
<< v[row][col];
out << std::endl;
}
return out;
}
/** \namespace ciphers
* \brief Algorithms for encryption and decryption
*/
namespace ciphers {
/** dictionary of characters that can be encrypted and decrypted */
static const char *STRKEY =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&"
"*()_+`-=[]{}|;':\",./<>?\\\r\n \0";
/**
* @brief Implementation of [Hill
* Cipher](https://en.wikipedia.org/wiki/Hill_cipher) algorithm
*/
class HillCipher {
private:
/**
* @brief Function to generate a random integer in a given interval
*
* @param a lower limit of interval
* @param b upper limit of interval
* @tparam T type of output
* @return random integer in the interval \f$[a,b)\f$
*/
template <typename T1, typename T2>
static const T2 rand_range(T1 a, T1 b) {
// generate random number between 0 and 1
long double r = static_cast<long double>(std::rand()) / RAND_MAX;
// scale and return random number as integer
return static_cast<T2>(r * (b - a) + a);
}
/**
* @brief Function overload to fill a matrix with random integers in a given
* interval
*
* @param M pointer to matrix to be filled with random numbers
* @param a lower limit of interval
* @param b upper limit of interval
* @tparam T1 type of input range
* @tparam T2 type of matrix
* @return determinant of generated random matrix
*
* @warning There will need to be a balance between the matrix size and the
* range of random numbers. If the matrix is large, the range of random
* numbers must be small to have a well defined keys. Or if the matrix is
* smaller, the random numbers range can be larger. For an 8x8 matrix, range
* should be no more than \f$[0,10]\f$
*/
template <typename T1, typename T2>
static double rand_range(matrix<T2> *M, T1 a, T1 b) {
for (size_t i = 0; i < M->size(); i++) {
for (size_t j = 0; j < M[0][0].size(); j++) {
M[0][i][j] = rand_range<T1, T2>(a, b);
}
}
return determinant_lu(*M);
}
/**
* @brief Compute
* [GCD](https://en.wikipedia.org/wiki/Greatest_common_divisor) of two
* integers using Euler's algorithm
*
* @param a first number
* @param b second number
* @return GCD of \f$a\f$ and \f$b\f$
*/
template <typename T>
static const T gcd(T a, T b) {
if (b > a) // ensure always a < b
std::swap(a, b);
while (b != 0) {
T tmp = b;
b = a % b;
a = tmp;
}
return a;
}
/**
* @brief helper function to perform vector multiplication with encryption
* or decryption matrix
*
* @param vector vector to multiply
* @param key encryption or decryption key matrix
* @return corresponding encrypted or decrypted text
*/
static const std::valarray<uint8_t> mat_mul(
const std::valarray<uint8_t> &vector, const matrix<int> &key) {
std::valarray<uint8_t> out(vector); // make a copy
size_t L = std::strlen(STRKEY);
for (size_t i = 0; i < key.size(); i++) {
int tmp = 0;
for (size_t j = 0; j < vector.size(); j++) {
tmp += key[i][j] * vector[j];
}
out[i] = static_cast<uint8_t>(tmp % L);
}
return out;
}
/**
* @brief Get the character at a given index in the ::STRKEY
*
* @param idx index value
* @return character at the index
*/
static inline char get_idx_char(const uint8_t idx) { return STRKEY[idx]; }
/**
* @brief Get the index of a character in the ::STRKEY
*
* @param ch character to search
* @return index of character
*/
static inline uint8_t get_char_idx(const char ch) {
size_t L = std::strlen(STRKEY);
for (size_t idx = 0; idx <= L; idx++)
if (STRKEY[idx] == ch)
return idx;
std::cerr << __func__ << ":" << __LINE__ << ": (" << ch
<< ") Should not reach here!\n";
return 0;
}
/**
* @brief Convenience function to perform block cipher operations. The
* operations are identical for both encryption and decryption.
*
* @param text input text to encrypt or decrypt
* @param key key for encryption or decryption
* @return encrypted/decrypted output
*/
static const std::string codec(const std::string &text,
const matrix<int> &key) {
size_t text_len = text.length();
size_t key_len = key.size();
// length of output string must be a multiple of key_len
// create output string and initialize with '\0' character
size_t L2 = text_len % key_len == 0
? text_len
: text_len + key_len - (text_len % key_len);
std::string coded_text(L2, '\0');
// temporary array for batch processing
int i;
#ifdef _OPENMP
#pragma parallel omp for private(i)
#endif
for (i = 0; i < L2 - key_len + 1; i += key_len) {
std::valarray<uint8_t> batch_int(key_len);
for (size_t j = 0; j < key_len; j++) {
batch_int[j] = get_char_idx(text[i + j]);
}
batch_int = mat_mul(batch_int, key);
for (size_t j = 0; j < key_len; j++) {
coded_text[i + j] =
STRKEY[batch_int[j]]; // get character at key
}
}
return coded_text;
}
/**
* Get matrix inverse using Row-transformations. Given matrix must
* be a square and non-singular.
* \returns inverse matrix
**/
template <typename T>
static matrix<double> get_inverse(matrix<T> const &A) {
// Assuming A is square matrix
size_t N = A.size();
matrix<double> inverse(N, std::valarray<double>(N));
for (size_t row = 0; row < N; row++) {
for (size_t col = 0; col < N; col++) {
// create identity matrix
inverse[row][col] = (row == col) ? 1.f : 0.f;
}
}
if (A.size() != A[0].size()) {
std::cerr << "A must be a square matrix!" << std::endl;
return inverse;
}
// preallocate a temporary matrix identical to A
matrix<double> temp(N, std::valarray<double>(N));
for (size_t row = 0; row < N; row++) {
for (size_t col = 0; col < N; col++)
temp[row][col] = static_cast<double>(A[row][col]);
}
// start transformations
for (size_t row = 0; row < N; row++) {
for (size_t row2 = row; row2 < N && temp[row][row] == 0; row2++) {
// this to ensure diagonal elements are not 0
temp[row] = temp[row] + temp[row2];
inverse[row] = inverse[row] + inverse[row2];
}
for (size_t col2 = row; col2 < N && temp[row][row] == 0; col2++) {
// this to further ensure diagonal elements are not 0
for (size_t row2 = 0; row2 < N; row2++) {
temp[row2][row] = temp[row2][row] + temp[row2][col2];
inverse[row2][row] =
inverse[row2][row] + inverse[row2][col2];
}
}
if (temp[row][row] == 0) {
// Probably a low-rank matrix and hence singular
std::cerr << "Low-rank matrix, no inverse!" << std::endl;
return inverse;
}
// set diagonal to 1
double divisor = temp[row][row];
temp[row] = temp[row] / divisor;
inverse[row] = inverse[row] / divisor;
// Row transformations
for (size_t row2 = 0; row2 < N; row2++) {
if (row2 == row)
continue;
double factor = temp[row2][row];
temp[row2] = temp[row2] - factor * temp[row];
inverse[row2] = inverse[row2] - factor * inverse[row];
}
}
return inverse;
}
static int modulo(int a, int b) {
int ret = a % b;
if (ret < 0)
ret += b;
return ret;
}
public:
/**
* @brief Generate encryption matrix of a given size. Larger size matrices
* are difficult to generate but provide more security. Important conditions
* are:
* 1. matrix should be invertible
* 2. determinant must not have any common factors with the length of
* character key
* There is no head-fast way to generate hte matrix under the given
* numerical restrictions of the machine but the conditions added achieve
* the goals. Bigger the matrix, greater is the probability of the matrix
* being ill-defined.
*
* @param size size of matrix (typically \f$\text{size}\le10\f$)
* @param limit1 lower limit of range of random elements (default=0)
* @param limit2 upper limit of range of random elements (default=10)
* @return Encryption martix
*/
static matrix<int> generate_encryption_key(size_t size, int limit1 = 0,
int limit2 = 10) {
matrix<int> encrypt_key(size, std::valarray<int>(size));
matrix<int> min_mat = encrypt_key;
int mat_determinant = -1; // because matrix has only ints, the
// determinant will also be an int
int L = std::strlen(STRKEY);
double dd;
do {
// keeping the random number range smaller generates better
// defined matrices with more ease of cracking
dd = rand_range(&encrypt_key, limit1, limit2);
mat_determinant = static_cast<int>(dd);
if (mat_determinant < 0)
mat_determinant = (mat_determinant % L);
} while (std::abs(dd) > 1e3 || // while ill-defined
dd < 0.1 || // while singular or negative determinant
!std::isfinite(dd) || // while determinant is not finite
gcd(mat_determinant, L) != 1); // while no common factors
// std::cout <<
return encrypt_key;
}
/**
* @brief Generate decryption matrix from an encryption matrix key.
*
* @param encrypt_key encryption key for which to create a decrypt key
* @return Decryption martix
*/
static matrix<int> generate_decryption_key(matrix<int> const &encrypt_key) {
size_t size = encrypt_key.size();
int L = std::strlen(STRKEY);
matrix<int> decrypt_key(size, std::valarray<int>(size));
int det_encrypt = static_cast<int>(determinant_lu(encrypt_key));
int mat_determinant = det_encrypt < 0 ? det_encrypt % L : det_encrypt;
matrix<double> tmp_inverse = get_inverse(encrypt_key);
double d2 = determinant_lu(decrypt_key);
// find co-prime factor for inversion
int det_inv = -1;
for (int i = 0; i < L; i++) {
if (modulo(mat_determinant * i, L) == 1) {
det_inv = i;
break;
}
}
if (det_inv == -1) {
std::cerr << "Could not find a co-prime for inversion\n";
std::exit(EXIT_FAILURE);
}
mat_determinant = det_inv * det_encrypt;
// perform modular inverse of encryption matrix
int i;
#ifdef _OPENMP
#pragma parallel omp for private(i)
#endif
for (i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
int temp = std::round(tmp_inverse[i][j] * mat_determinant);
decrypt_key[i][j] = modulo(temp, L);
}
}
return decrypt_key;
}
/**
* @brief Generate encryption and decryption key pair
*
* @param size size of matrix key (typically \f$\text{size}\le10\f$)
* @param limit1 lower limit of range of random elements (default=0)
* @param limit2 upper limit of range of random elements (default=10)
* @return std::pair<matrix<int>, matrix<int>> encryption and decryption
* keys as a pair
*
* @see ::generate_encryption_key
*/
static std::pair<matrix<int>, matrix<int>> generate_keys(size_t size,
int limit1 = 0,
int limit2 = 10) {
matrix<int> encrypt_key = generate_encryption_key(size);
matrix<int> decrypt_key = generate_decryption_key(encrypt_key);
double det2 = determinant_lu(decrypt_key);
while (std::abs(det2) < 0.1 || std::abs(det2) > 1e3) {
encrypt_key = generate_encryption_key(size, limit1, limit2);
decrypt_key = generate_decryption_key(encrypt_key);
det2 = determinant_lu(decrypt_key);
}
return std::make_pair(encrypt_key, decrypt_key);
}
/**
* @brief Encrypt a given text using a given key
*
* @param text string to encrypt
* @param encrypt_key key for encryption
* @return encrypted text
*/
static const std::string encrypt_text(const std::string &text,
const matrix<int> &encrypt_key) {
return codec(text, encrypt_key);
}
/**
* @brief Decrypt a given text using a given key
*
* @param text string to decrypt
* @param decrypt_key key for decryption
* @return decrypted text
*/
static const std::string decrypt_text(const std::string &text,
const matrix<int> &decrypt_key) {
return codec(text, decrypt_key);
}
};
} // namespace ciphers
/**
* @brief Self test 1 - using 3x3 randomly generated key
*
* @param text string to encrypt and decrypt
*/
void test1(const std::string &text) {
// std::string text = "Hello world!";
std::cout << "======Test 1 (3x3 key) ======\nOriginal text:\n\t" << text
<< std::endl;
std::pair<matrix<int>, matrix<int>> p =
ciphers::HillCipher::generate_keys(3, 0, 100);
matrix<int> ekey = p.first;
matrix<int> dkey = p.second;
// matrix<int> ekey = {{22, 28, 25}, {5, 26, 15}, {14, 18, 9}};
// std::cout << "Encryption key: \n" << ekey;
std::string gibberish = ciphers::HillCipher::encrypt_text(text, ekey);
std::cout << "Encrypted text:\n\t" << gibberish << std::endl;
// matrix<int> dkey = ciphers::HillCipher::generate_decryption_key(ekey);
// std::cout << "Decryption key: \n" << dkey;
std::string txt_back = ciphers::HillCipher::decrypt_text(gibberish, dkey);
std::cout << "Reconstruct text:\n\t" << txt_back << std::endl;
std::ofstream out_file("hill_cipher_test1.txt");
out_file << "Block size: " << ekey.size() << "\n";
out_file << "Encryption Key:\n" << ekey;
out_file << "\nDecryption Key:\n" << dkey;
out_file.close();
assert(txt_back == text);
std::cout << "Passed :)\n";
}
/**
* @brief Self test 2 - using 8x8 randomly generated key
*
* @param text string to encrypt and decrypt
*/
void test2(const std::string &text) {
// std::string text = "Hello world!";
std::cout << "======Test 2 (8x8 key) ======\nOriginal text:\n\t" << text
<< std::endl;
std::pair<matrix<int>, matrix<int>> p =
ciphers::HillCipher::generate_keys(8, 0, 3);
matrix<int> ekey = p.first;
matrix<int> dkey = p.second;
std::string gibberish = ciphers::HillCipher::encrypt_text(text, ekey);
std::cout << "Encrypted text:\n\t" << gibberish << std::endl;
std::string txt_back = ciphers::HillCipher::decrypt_text(gibberish, dkey);
std::cout << "Reconstruct text:\n\t" << txt_back << std::endl;
std::ofstream out_file("hill_cipher_test2.txt");
out_file << "Block size: " << ekey.size() << "\n";
out_file << "Encryption Key:\n" << ekey;
out_file << "\nDecryption Key:\n" << dkey;
out_file.close();
assert(txt_back.compare(0, text.size(), text) == 0);
std::cout << "Passed :)\n";
}
/** Main function */
int main() {
std::srand(std::time(nullptr));
std::cout << "Key dictionary: (" << std::strlen(ciphers::STRKEY) << ")\n\t"
<< ciphers::STRKEY << "\n";
std::string text = "This is a simple text with numb3r5 and exclamat!0n.";
test1(text);
test2(text);
return 0;
}

View File

@@ -14,17 +14,17 @@ struct node {
node *right;
};
struct queue {
struct Queue {
node *t[100];
int front;
int rear;
};
queue q;
Queue queue;
void enqueue(node *n) { q.t[q.rear++] = n; }
void enqueue(node *n) { queue.t[queue.rear++] = n; }
node *dequeue() { return (q.t[q.front++]); }
node *dequeue() { return (queue.t[queue.front++]); }
void Insert(node *n, int x) {
if (x < n->val) {
@@ -43,7 +43,7 @@ void Insert(node *n, int x) {
temp->val = x;
temp->left = NULL;
temp->right = NULL;
n->left = temp;
n->right = temp;
} else {
Insert(n->right, x);
}
@@ -123,8 +123,8 @@ void Post(node *n) {
}
int main() {
q.front = 0;
q.rear = 0;
queue.front = 0;
queue.rear = 0;
int value;
int ch;
node *root = new node;

View File

@@ -13,13 +13,16 @@ class MinHeap {
int heap_size; ///< Current number of elements in min heap
public:
/** Constructor
/** Constructor: Builds a heap from a given array a[] of given size
* \param[in] capacity initial heap capacity
*/
MinHeap(int capacity);
explicit MinHeap(int cap) {
heap_size = 0;
capacity = cap;
harr = new int[cap];
}
/** to heapify a subtree with the root at given index
*/
/** to heapify a subtree with the root at given index */
void MinHeapify(int);
int parent(int i) { return (i - 1) / 2; }
@@ -44,14 +47,9 @@ class MinHeap {
/** Inserts a new key 'k' */
void insertKey(int k);
};
/** Constructor: Builds a heap from a given array a[] of given size */
MinHeap::MinHeap(int cap) {
heap_size = 0;
capacity = cap;
harr = new int[cap];
}
~MinHeap() { delete[] harr; }
};
// Inserts a new key 'k'
void MinHeap::insertKey(int k) {

View File

@@ -1,3 +1,24 @@
/**
*
* \file
* \brief [Disjoint Sets Data Structure
* (Disjoint Sets)](https://en.wikipedia.org/wiki/Disjoint-set_data_structure)
*
* \author [leoyang429](https://github.com/leoyang429)
*
* \details
* A disjoint set data structure (also called union find or merge find set)
* is a data structure that tracks a set of elements partitioned into a number
* of disjoint (non-overlapping) subsets.
* Some situations where disjoint sets can be used are-
* to find connected components of a graph, kruskal's algorithm for finding
* Minimum Spanning Tree etc.
* There are two operation which we perform on disjoint sets -
* 1) Union
* 2) Find
*
*/
#include <iostream>
#include <vector>
@@ -5,16 +26,30 @@ using std::cout;
using std::endl;
using std::vector;
vector<int> root, rnk;
vector<int> root, rank;
/**
*
* Function to create a set
* @param n number of element
*
*/
void CreateSet(int n) {
root = vector<int>(n + 1);
rnk = vector<int>(n + 1, 1);
rank = vector<int>(n + 1, 1);
for (int i = 1; i <= n; ++i) {
root[i] = i;
}
}
/**
*
* Find operation takes a number x and returns the set to which this number
* belongs to.
* @param x element of some set
* @return set to which x belongs to
*
*/
int Find(int x) {
if (root[x] == x) {
return x;
@@ -22,22 +57,39 @@ int Find(int x) {
return root[x] = Find(root[x]);
}
/**
*
* A utility function to check if x and y are from same set or not
* @param x element of some set
* @param y element of some set
*
*/
bool InSameUnion(int x, int y) { return Find(x) == Find(y); }
/**
*
* Union operation combines two disjoint sets to make a single set
* in this union function we pass two elements and check if they are
* from different sets then combine those sets
* @param x element of some set
* @param y element of some set
*
*/
void Union(int x, int y) {
int a = Find(x), b = Find(y);
if (a != b) {
if (rnk[a] < rnk[b]) {
if (rank[a] < rank[b]) {
root[a] = b;
} else if (rnk[a] > rnk[b]) {
} else if (rank[a] > rank[b]) {
root[b] = a;
} else {
root[a] = b;
++rnk[b];
++rank[b];
}
}
}
/** Main function */
int main() {
// tests CreateSet & Find
int n = 100;

View File

@@ -1,5 +1,9 @@
/**
* @file list_array.cpp
* @todo Add documentation
* @warning The sorting algorithm is erroneous
*/
#include <iostream>
using namespace std;
struct list {
int data[50];
@@ -17,6 +21,9 @@ struct list {
return (BinarySearch(array, first, mid - 1, x));
else if (x > array[mid])
return (BinarySearch(array, mid + 1, last, x));
std::cerr << __func__ << ":" << __LINE__ << ": Undefined condition\n";
return -1;
}
int LinarSearch(int *array, int x) {
@@ -34,16 +41,14 @@ struct list {
if (isSorted) {
pos = BinarySearch(data, 0, top - 1, x);
}
else {
} else {
pos = LinarSearch(data, x);
}
if (pos != -1) {
cout << "\nElement found at position : " << pos;
std::cout << "\nElement found at position : " << pos;
} else {
cout << "\nElement not found";
std::cout << "\nElement not found";
}
return pos;
}
@@ -69,14 +74,12 @@ struct list {
void insert(int x) {
if (!isSorted) {
if (top == 49) {
cout << "\nOverflow";
std::cout << "\nOverflow";
} else {
data[top] = x;
top++;
}
}
else {
} else {
int pos = 0;
for (int i = 0; i < top - 1; i++) {
@@ -99,7 +102,7 @@ struct list {
void Remove(int x) {
int pos = Search(x);
cout << "\n" << data[pos] << " deleted";
std::cout << "\n" << data[pos] << " deleted";
for (int i = pos; i < top; i++) {
data[i] = data[i + 1];
}
@@ -108,7 +111,7 @@ struct list {
void Show() {
for (int i = 0; i < top; i++) {
cout << data[i] << "\t";
std::cout << data[i] << "\t";
}
}
};
@@ -118,27 +121,27 @@ int main() {
int choice;
int x;
do {
cout << "\n1.Insert";
cout << "\n2.Delete";
cout << "\n3.Search";
cout << "\n4.Sort";
cout << "\n5.Print";
cout << "\n\nEnter Your Choice : ";
cin >> choice;
std::cout << "\n1.Insert";
std::cout << "\n2.Delete";
std::cout << "\n3.Search";
std::cout << "\n4.Sort";
std::cout << "\n5.Print";
std::cout << "\n\nEnter Your Choice : ";
std::cin >> choice;
switch (choice) {
case 1:
cout << "\nEnter the element to be inserted : ";
cin >> x;
std::cout << "\nEnter the element to be inserted : ";
std::cin >> x;
L.insert(x);
break;
case 2:
cout << "\nEnter the element to be removed : ";
cin >> x;
std::cout << "\nEnter the element to be removed : ";
std::cin >> x;
L.Remove(x);
break;
case 3:
cout << "\nEnter the element to be searched : ";
cin >> x;
std::cout << "\nEnter the element to be searched : ";
std::cin >> x;
L.Search(x);
break;
case 4:

View File

@@ -0,0 +1,144 @@
/**
* @author [shoniavika](https://github.com/shoniavika)
* @file
*
* Implementation of a Queue using two Stacks.
*/
#include <cassert>
#include <iostream>
#include <stack>
namespace {
/**
* @brief Queue data structure. Stores elements in FIFO
* (first-in-first-out) manner.
* @tparam T datatype to store in the queue
*/
template <typename T>
class MyQueue {
private:
std::stack<T> s1, s2;
public:
/**
* Constructor for queue.
*/
MyQueue() = default;
/**
* Pushes x to the back of queue.
*/
void push(T x);
/**
* Removes an element from the front of the queue.
*/
const T& pop();
/**
* Returns first element, without removing it.
*/
const T& peek() const;
/**
* Returns whether the queue is empty.
*/
bool empty() const;
};
/**
* Appends element to the end of the queue
*/
template <typename T>
void MyQueue<T>::push(T x) {
while (!s2.empty()) {
s1.push(s2.top());
s2.pop();
}
s2.push(x);
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
/**
* Removes element from the front of the queue
*/
template <typename T>
const T& MyQueue<T>::pop() {
const T& temp = MyQueue::peek();
s2.pop();
return temp;
}
/**
* Returns element in the front.
* Does not remove it.
*/
template <typename T>
const T& MyQueue<T>::peek() const {
if (!empty()) {
return s2.top();
}
std::cerr << "Queue is empty" << std::endl;
exit(0);
}
/**
* Checks whether a queue is empty
*/
template <typename T>
bool MyQueue<T>::empty() const {
return s2.empty() && s1.empty();
}
} // namespace
/**
* Testing function
*/
void queue_test() {
MyQueue<int> que;
std::cout << "Test #1\n";
que.push(2);
que.push(5);
que.push(0);
assert(que.peek() == 2);
assert(que.pop() == 2);
assert(que.peek() == 5);
assert(que.pop() == 5);
assert(que.peek() == 0);
assert(que.pop() == 0);
assert(que.empty() == true);
std::cout << "PASSED\n";
std::cout << "Test #2\n";
que.push(-1);
assert(que.empty() == false);
assert(que.peek() == -1);
assert(que.pop() == -1);
std::cout << "PASSED\n";
MyQueue<double> que2;
std::cout << "Test #3\n";
que2.push(2.31223);
que2.push(3.1415926);
que2.push(2.92);
assert(que2.peek() == 2.31223);
assert(que2.pop() == 2.31223);
assert(que2.peek() == 3.1415926);
assert(que2.pop() == 3.1415926);
assert(que2.peek() == 2.92);
assert(que2.pop() == 2.92);
std::cout << "PASSED\n";
}
/**
* Main function, calls testing function
*/
int main() {
queue_test();
return 0;
}

View File

@@ -0,0 +1,221 @@
/**
* @file skip_list.cpp
* @brief Data structure for fast searching and insertion in \f$O(\log n)\f$
* time
* @details
* A skip list is a data structure that is used for storing a sorted list of
* items with a help of hierarchy of linked lists that connect increasingly
* sparse subsequences of the items
*
* References used: [GeeksForGeek](https://www.geeksforgeeks.org/skip-list/),
* [OpenGenus](https://iq.opengenus.org/skip-list) for PseudoCode and Code
* @author [enqidu](https://github.com/enqidu)
* @author [Krishna Vedala](https://github.com/kvedala)
*/
#include <array>
#include <cstring>
#include <ctime>
#include <iostream>
#include <memory>
#include <vector>
/** \namespace data_structure
* \brief Data-structure algorithms
*/
namespace data_structure {
constexpr int MAX_LEVEL = 2; ///< Maximum level of skip list
constexpr float PROBABILITY = 0.5; ///< Current probability for "coin toss"
/**
* Node structure [Key][Node*, Node*...]
*/
struct Node {
int key; ///< key integer
void* value; ///< pointer of value
std::vector<std::shared_ptr<Node>>
forward; ///< nodes of the given one in all levels
/**
* Creates node with provided key, level and value
* @param key is number that is used for comparision
* @param level is the maximum level node's going to added
*/
Node(int key, int level, void* value = nullptr) : key(key), value(value) {
// Initialization of forward vector
for (int i = 0; i < (level + 1); i++) {
forward.push_back(nullptr);
}
}
};
/**
* SkipList class implementation with basic methods
*/
class SkipList {
int level; ///< Maximum level of the skiplist
std::shared_ptr<Node> header; ///< Pointer to the header node
public:
/**
* Skip List constructor. Initializes header, start
* Node for searching in the list
*/
SkipList() {
level = 0;
// Header initialization
header = std::shared_ptr<Node>(new Node(-1, MAX_LEVEL));
}
/**
* Returns random level of the skip list.
* Every higher level is 2 times less likely.
* @return random level for skip list
*/
int randomLevel() {
int lvl = 0;
while (static_cast<float>(std::rand()) / RAND_MAX < PROBABILITY &&
lvl < MAX_LEVEL)
lvl++;
return lvl;
}
/**
* Inserts elements with given key and value;
* It's level is computed by randomLevel() function.
* @param key is number that is used for comparision
* @param value pointer to a value, that can be any type
*/
void insertElement(int key, void* value) {
std::cout << "Inserting" << key << "...";
std::shared_ptr<Node> x = header;
std::array<std::shared_ptr<Node>, MAX_LEVEL + 1> update;
update.fill(nullptr);
for (int i = level; i >= 0; i--) {
while (x->forward[i] != nullptr && x->forward[i]->key < key)
x = x->forward[i];
update[i] = x;
}
x = x->forward[0];
bool doesnt_exist = (x == nullptr || x->key != key);
if (doesnt_exist) {
int rlevel = randomLevel();
if (rlevel > level) {
for (int i = level + 1; i < rlevel + 1; i++) update[i] = header;
// Update current level
level = rlevel;
}
std::shared_ptr<Node> n =
std::shared_ptr<Node>(new Node(key, rlevel, value));
for (int i = 0; i <= rlevel; i++) {
n->forward[i] = update[i]->forward[i];
update[i]->forward[i] = n;
}
std::cout << "Inserted" << std::endl;
} else {
std::cout << "Exists" << std::endl;
}
}
/**
* Deletes an element by key and prints if has been removed successfully
* @param key is number that is used for comparision.
*/
void deleteElement(int key) {
std::shared_ptr<Node> x = header;
std::array<std::shared_ptr<Node>, MAX_LEVEL + 1> update;
update.fill(nullptr);
for (int i = level; i >= 0; i--) {
while (x->forward[i] != nullptr && x->forward[i]->key < key)
x = x->forward[i];
update[i] = x;
}
x = x->forward[0];
bool doesnt_exist = (x == nullptr || x->key != key);
if (!doesnt_exist) {
for (int i = 0; i <= level; i++) {
if (update[i]->forward[i] != x)
break;
update[i]->forward[i] = x->forward[i];
}
/* Remove empty levels*/
while (level > 0 && header->forward[level] == nullptr) level--;
std::cout << "Deleted" << std::endl;
} else {
std::cout << "Doesn't exist" << std::endl;
}
}
/**
* Searching element in skip list structure
* @param key is number that is used for comparision
* @return pointer to the value of the node
*/
void* searchElement(int key) {
std::shared_ptr<Node> x = header;
std::cout << "Searching for " << key << std::endl;
for (int i = level; i >= 0; i--) {
while (x->forward[i] && x->forward[i]->key < key) x = x->forward[i];
}
x = x->forward[0];
if (x && x->key == key) {
std::cout << "Found" << std::endl;
return x->value;
} else {
std::cout << "Not Found" << std::endl;
return nullptr;
}
}
/**
* Display skip list level
*/
void displayList() {
std::cout << "Displaying list:\n";
for (int i = 0; i <= level; i++) {
std::shared_ptr<Node> node = header->forward[i];
std::cout << "Level " << (i) << ": ";
while (node != nullptr) {
std::cout << node->key << " ";
node = node->forward[i];
}
std::cout << std::endl;
}
}
};
} // namespace data_structure
/**
* Main function:
* Creates and inserts random 2^[number of levels]
* elements into the skip lists and than displays it
*/
int main() {
std::srand(std::time(nullptr));
data_structure::SkipList lst;
for (int j = 0; j < (1 << (data_structure::MAX_LEVEL + 1)); j++) {
int k = (std::rand() % (1 << (data_structure::MAX_LEVEL + 2)) + 1);
lst.insertElement(k, &j);
}
lst.displayList();
return 0;
}

View File

@@ -1,18 +1,27 @@
/* This class specifies the basic operation on a stack as a linked list */
/**
* @file stack.h
* @author danghai
* @brief This class specifies the basic operation on a stack as a linked list
**/
#ifndef DATA_STRUCTURES_STACK_H_
#define DATA_STRUCTURES_STACK_H_
#include <cassert>
#include <iostream>
/* Definition of the node */
/** Definition of the node as a linked-list
* \tparam Type type of data nodes of the linked list should contain
*/
template <class Type>
struct node {
Type data;
node<Type> *next;
Type data; ///< data at current node
node<Type> *next; ///< pointer to the next ::node instance
};
/* Definition of the stack class */
/** Definition of the stack class
* \tparam Type type of data nodes of the linked list in the stack should
* contain
*/
template <class Type>
class stack {
public:
@@ -20,7 +29,7 @@ class stack {
void display() {
node<Type> *current = stackTop;
std::cout << "Top --> ";
while (current != NULL) {
while (current != nullptr) {
std::cout << current->data << " ";
current = current->next;
}
@@ -30,15 +39,45 @@ class stack {
/** Default constructor*/
stack() {
stackTop = NULL;
stackTop = nullptr;
size = 0;
}
/** Copy constructor*/
explicit stack(const stack<Type> &otherStack) {
node<Type> *newNode, *current, *last;
/* If stack is no empty, make it empty */
if (stackTop != nullptr) {
stackTop = nullptr;
}
if (otherStack.stackTop == nullptr) {
stackTop = nullptr;
} else {
current = otherStack.stackTop;
stackTop = new node<Type>;
stackTop->data = current->data;
stackTop->next = nullptr;
last = stackTop;
current = current->next;
/* Copy the remaining stack */
while (current != nullptr) {
newNode = new node<Type>;
newNode->data = current->data;
newNode->next = nullptr;
last->next = newNode;
last = newNode;
current = current->next;
}
}
size = otherStack.size;
}
/** Destructor */
~stack() {}
/** Determine whether the stack is empty */
bool isEmptyStack() { return (stackTop == NULL); }
bool isEmptyStack() { return (stackTop == nullptr); }
/** Add new item to the stack */
void push(Type item) {
@@ -52,7 +91,7 @@ class stack {
/** Return the top element of the stack */
Type top() {
assert(stackTop != NULL);
assert(stackTop != nullptr);
return stackTop->data;
}
@@ -70,30 +109,30 @@ class stack {
}
/** Clear stack */
void clear() { stackTop = NULL; }
void clear() { stackTop = nullptr; }
/** Overload "=" the assignment operator */
stack<Type> &operator=(const stack<Type> &otherStack) {
node<Type> *newNode, *current, *last;
/* If stack is no empty, make it empty */
if (stackTop != NULL) {
stackTop = NULL;
if (stackTop != nullptr) {
stackTop = nullptr;
}
if (otherStack.stackTop == NULL) {
stackTop = NULL;
if (otherStack.stackTop == nullptr) {
stackTop = nullptr;
} else {
current = otherStack.stackTop;
stackTop = new node<Type>;
stackTop->data = current->data;
stackTop->next = NULL;
stackTop->next = nullptr;
last = stackTop;
current = current->next;
/* Copy the remaining stack */
while (current != NULL) {
while (current != nullptr) {
newNode = new node<Type>;
newNode->data = current->data;
newNode->next = NULL;
newNode->next = nullptr;
last->next = newNode;
last = newNode;
current = current->next;
@@ -105,7 +144,7 @@ class stack {
private:
node<Type> *stackTop; /**< Pointer to the stack */
int size;
int size; ///< size of stack
};
#endif // DATA_STRUCTURES_STACK_H_

View File

@@ -1,37 +1,38 @@
#include <iostream>
int *stack;
int top = 0, stack_size;
int stack_idx = 0, stack_size;
void push(int x) {
if (top == stack_size) {
if (stack_idx == stack_size) {
std::cout << "\nOverflow";
} else {
stack[top++] = x;
stack[stack_idx++] = x;
}
}
void pop() {
if (top == 0) {
if (stack_idx == 0) {
std::cout << "\nUnderflow";
} else {
std::cout << "\n" << stack[--top] << " deleted";
std::cout << "\n" << stack[--stack_idx] << " deleted";
}
}
void show() {
for (int i = 0; i < top; i++) {
for (int i = 0; i < stack_idx; i++) {
std::cout << stack[i] << "\n";
}
}
void topmost() { std::cout << "\nTopmost element: " << stack[top - 1]; }
void topmost() { std::cout << "\nTopmost element: " << stack[stack_idx - 1]; }
int main() {
std::cout << "\nEnter stack_size of stack : ";
std::cin >> stack_size;
stack = new int[stack_size];
int ch, x;
do {
std::cout << "\n0. Exit";
std::cout << "\n1. Push";
std::cout << "\n2. Pop";
std::cout << "\n3. Print";
@@ -51,5 +52,7 @@ int main() {
}
} while (ch != 0);
delete[] stack;
return 0;
}

View File

@@ -1,35 +1,34 @@
#include <iostream>
using namespace std;
struct node {
int val;
node *next;
};
node *top;
node *top_var;
void push(int x) {
node *n = new node;
n->val = x;
n->next = top;
top = n;
n->next = top_var;
top_var = n;
}
void pop() {
if (top == NULL) {
cout << "\nUnderflow";
if (top_var == NULL) {
std::cout << "\nUnderflow";
} else {
node *t = top;
cout << "\n" << t->val << " deleted";
top = top->next;
node *t = top_var;
std::cout << "\n" << t->val << " deleted";
top_var = top_var->next;
delete t;
}
}
void show() {
node *t = top;
node *t = top_var;
while (t != NULL) {
cout << t->val << "\n";
std::cout << t->val << "\n";
t = t->next;
}
}
@@ -37,14 +36,14 @@ void show() {
int main() {
int ch, x;
do {
cout << "\n1. Push";
cout << "\n2. Pop";
cout << "\n3. Print";
cout << "\nEnter Your Choice : ";
cin >> ch;
std::cout << "\n1. Push";
std::cout << "\n2. Pop";
std::cout << "\n3. Print";
std::cout << "\nEnter Your Choice : ";
std::cin >> ch;
if (ch == 1) {
cout << "\nInsert : ";
cin >> x;
std::cout << "\nInsert : ";
std::cin >> x;
push(x);
} else if (ch == 2) {
pop();

View File

@@ -1,7 +1,7 @@
/**
* @file
*
* Copyright 2020 @author Anmol3299
* @author Anmol3299
* \brief A basic implementation of trie class to store only lower-case strings.
*/
#include <iostream> // for io operations

View File

@@ -74,6 +74,10 @@ bool deleteString(trie* root, std::string str, int index) {
return true;
}
}
/* should not return here */
std::cout << __func__ << ":" << __LINE__ << "Should not reach this line\n";
return false;
}
int main() {

View File

@@ -1,29 +1,85 @@
/**
*
* \file
* \brief [Graph Connected Components
* (Connected Components)]
* (https://en.wikipedia.org/wiki/Component_(graph_theory))
*
* \author [Ayaan Khan](http://github.com/ayaankhan98)
*
* \details
* A graph is a collection of nodes also called vertices and these vertices
* are connected by edges. A connected component in a graph refers to a set of
* vertices which are reachable form one another.
*
* <pre>
* Example - Here is graph with 3 connected components
*
* 3 9 6 8
* / \ / / \ / \
* 2---4 2 7 3 7
*
* first second third
* component component component
* </pre>
*
*/
#include <algorithm>
#include <iostream>
#include <vector>
using std::vector;
/**
* Class for representing graph as a adjacency list.
*/
class graph {
private:
/** \brief adj stores adjacency list representation of graph */
vector<vector<int>> adj;
/** \brief keep track of connected components */
int connected_components;
void depth_first_search();
void explore(int, vector<bool> &);
public:
/**
* \brief Constructor that intiliazes the graph on creation and set
* the connected components to 0
*/
explicit graph(int n) : adj(n, vector<int>()) { connected_components = 0; }
void addEdge(int, int);
/**
* \brief Function the calculates the connected compoents in the graph
* by performing the depth first search on graph
*
* @return connected_components total connected components in graph
*/
int getConnectedComponents() {
depth_first_search();
return connected_components;
}
};
/**
* \brief Function that add edge between two nodes or vertices of graph
*
* @param u any node or vertex of graph
* @param v any node or vertex of graph
*/
void graph::addEdge(int u, int v) {
adj[u - 1].push_back(v - 1);
adj[v - 1].push_back(u - 1);
}
/**
* \brief Function that perfoms depth first search algorithm on graph
*/
void graph::depth_first_search() {
int n = adj.size();
vector<bool> visited(n, false);
@@ -35,7 +91,13 @@ void graph::depth_first_search() {
}
}
}
/**
* \brief Utility function for depth first seach algorithm
* this function explores the vertex which is passed into.
*
* @param u vertex or node to be explored
* @param visited already visited vertex
*/
void graph::explore(int u, vector<bool> &visited) {
visited[u] = true;
for (auto v : adj[u]) {
@@ -45,10 +107,16 @@ void graph::explore(int u, vector<bool> &visited) {
}
}
/** Main function */
int main() {
/// creating a graph with 4 vertex
graph g(4);
/// Adding edges between vertices
g.addEdge(1, 2);
g.addEdge(3, 2);
/// printing the connected components
std::cout << g.getConnectedComponents();
return 0;
}

83
graphics/CMakeLists.txt Normal file
View File

@@ -0,0 +1,83 @@
find_package(OpenGL)
if(OpenGL_FOUND)
find_package(GLUT)
if(NOT GLUT_FOUND)
message("FreeGLUT library will be downloaded and built.")
include(ExternalProject)
ExternalProject_Add (
FREEGLUT-PRJ
URL https://sourceforge.net/projects/freeglut/files/freeglut/3.2.1/freeglut-3.2.1.tar.gz
URL_MD5 cd5c670c1086358598a6d4a9d166949d
CMAKE_GENERATOR ${CMAKE_GENERATOR}
CMAKE_GENERATOR_TOOLSET ${CMAKE_GENERATOR_TOOLSET}
CMAKE_GENERATOR_PLATFORM ${CMAKE_GENERATOR_PLATFORM}
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release
-DFREEGLUT_BUILD_SHARED_LIBS=OFF
-DFREEGLUT_BUILD_STATIC_LIBS=ON
-DFREEGLUT_BUILD_DEMOS=OFF
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/freeglut
# BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/freeglut-build
# BUILD_IN_SOURCE ON
# UPDATE_COMMAND ""
INSTALL_COMMAND ""
# CONFIGURE_COMMAND ""
# BUILD_COMMAND ""
)
ExternalProject_Get_Property(FREEGLUT-PRJ SOURCE_DIR)
ExternalProject_Get_Property(FREEGLUT-PRJ BINARY_DIR)
set(FREEGLUT_BIN_DIR ${BINARY_DIR})
set(FREEGLUT_SRC_DIR ${SOURCE_DIR})
# add_library(libfreeglut STATIC IMPORTED)
# set_target_properties(libfreeglut PROPERTIES IMPORTED_LOCATION ${FREEGLUT_BIN_DIR})
# set(FREEGLUT_BUILD_DEMOS OFF CACHE BOOL "")
# set(FREEGLUT_BUILD_SHARED_LIBS OFF CACHE BOOL "")
# set(FREEGLUT_BUILD_STATIC_LIBS ON CACHE BOOL "")
# add_subdirectory(${FREEGLUT_SRC_DIR} ${FREEGLUT_BIN_DIR} EXCLUDE_FROM_ALL)
# add_subdirectory(${BINARY_DIR})
# find_package(FreeGLUT)
endif(NOT GLUT_FOUND)
else(OpenGL_FOUND)
message(WARNING "OPENGL not found. Will not build graphical outputs.")
endif(OpenGL_FOUND)
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
# with full pathname. RELATIVE may makes it easier to extract an executable name
# automatically.
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
foreach( testsourcefile ${APP_SOURCES} )
# I used a simple string replace, to cut off .cpp.
string( REPLACE ".cpp" "" testname ${testsourcefile} )
add_executable( ${testname} ${testsourcefile} )
set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX)
if(OpenMP_CXX_FOUND)
target_link_libraries(${testname} PRIVATE OpenMP::OpenMP_CXX)
endif()
if(OpenGL_FOUND)
if(NOT GLUT_FOUND)
add_dependencies(${testname} FREEGLUT-PRJ)
target_compile_definitions(${testname} PRIVATE FREEGLUT_STATIC)
target_include_directories(${testname} PRIVATE ${FREEGLUT_SRC_DIR}/include)
target_link_directories(${testname} PRIVATE ${FREEGLUT_BIN_DIR}/lib)
target_link_libraries(${testname} PRIVATE OpenGL::GL)
target_link_libraries(${testname} INTERFACE FREEGLUT-PRJ)
# target_include_directories(${testname} PRIVATE ${FREEGLUT_INCLUDE_DIRS})
# target_link_libraries(${testname} INTERFACE freeglut_static)
else()
target_include_directories(${testname} PRIVATE ${GLUT_INCLUDE_DIRS})
target_link_libraries(${testname} PRIVATE OpenGL::GL ${GLUT_LIBRARIES})
endif()
target_compile_definitions(${testname} PRIVATE USE_GLUT)
endif(OpenGL_FOUND)
if(APPLE)
target_compile_options(${testname} PRIVATE -Wno-deprecated)
endif(APPLE)
install(TARGETS ${testname} DESTINATION "bin/graphics")
endforeach( testsourcefile ${APP_SOURCES} )

284
graphics/spirograph.cpp Normal file
View File

@@ -0,0 +1,284 @@
/**
* @file
* @author [Krishna Vedala](https://github.com/kvedala)
* @brief Implementation of
* [Spirograph](https://en.wikipedia.org/wiki/Spirograph)
*
* @details
* Implementation of the program is based on the geometry shown in the figure
* below:
*
* <a
* href="https://commons.wikimedia.org/wiki/File:Resonance_Cascade.svg"><img
* src="https://upload.wikimedia.org/wikipedia/commons/3/39/Resonance_Cascade.svg"
* alt="Spirograph geometry from Wikipedia" style="width: 250px"/></a>
*/
#ifdef USE_GLUT
#ifdef __APPLE__
#include <GLUT/glut.h> // include path on Macs is different
#else
#include <GL/glut.h>
#endif // __APPLE__
#endif
#define _USE_MATH_DEFINES /**< required for MSVC compiler */
#include <array>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#ifdef _OPENMP
#include <omp.h>
#endif
/**
* @namespace spirograph Functions related to spirograph.cpp
*/
namespace spirograph {
/** Generate spirograph curve into arrays `x` and `y` such that the i^th point
* in 2D is represented by `(x[i],y[i])`. The generating function is given by:
* \f{eqnarray*}{
* x &=& R\left[ (1-k) \cos (t) + l\cdot k\cdot\cos \left(\frac{1-k}{k}t\right)
* \right]\\
* y &=& R\left[ (1-k) \sin (t) - l\cdot k\cdot\sin \left(\frac{1-k}{k}t\right)
* \right] \f}
* where
* * \f$R\f$ is the scaling parameter that we will consider \f$=1\f$
* * \f$l=\frac{\rho}{r}\f$ is the relative distance of marker from the centre
* of inner circle and \f$0\le l\le1\f$
* * \f$\rho\f$ is physical distance of marker from centre of inner circle
* * \f$r\f$ is the radius of inner circle
* * \f$k=\frac{r}{R}\f$ is the ratio of radius of inner circle to outer circle
* and \f$0<k<1\f$
* * \f$R\f$ is the radius of outer circle
* * \f$t\f$ is the angle of rotation of the point i.e., represents the time
* parameter
*
* Since we are considering ratios, the actual values of \f$r\f$ and
* \f$R\f$ are immaterial.
*
* @tparam N number of points = size of array
* @param [out] points Array of 2D points represented as std::pair
* @param l the relative distance of marker from the centre of
* inner circle and \f$0\le l\le1\f$
* @param k the ratio of radius of inner circle to outer circle and \f$0<k<1\f$
* @param rot the number of rotations to perform (can be fractional value)
*/
template <std::size_t N>
void spirograph(std::array<std::pair<double, double>, N> *points, double l,
double k, double rot) {
double dt = rot * 2.f * M_PI / N;
double R = 1.f;
const double k1 = 1.f - k;
int32_t step = 0;
#ifdef _OPENMP
#pragma omp for
#endif
for (step = 0; step < N; step++) {
double t = dt * step;
double first = R * (k1 * std::cos(t) + l * k * std::cos(k1 * t / k));
double second = R * (k1 * std::sin(t) - l * k * std::sin(k1 * t / k));
points[0][step].first = first;
points[0][step].second = second;
}
}
/**
* @brief Test function to save resulting points to a CSV file.
*
*/
void test() {
const size_t N = 500;
double l = 0.3, k = 0.75, rot = 10.;
std::stringstream fname;
fname << std::setw(3) << "spirograph_" << l << "_" << k << "_" << rot
<< ".csv";
std::ofstream fp(fname.str());
if (!fp.is_open()) {
perror(fname.str().c_str());
exit(EXIT_FAILURE);
}
std::array<std::pair<double, double>, N> points;
spirograph(&points, l, k, rot);
for (size_t i = 0; i < N; i++) {
fp << points[i].first << "," << points[i].first;
if (i < N - 1) {
fp << '\n';
}
}
fp.close();
}
#ifdef USE_GLUT
static bool paused = 0; /**< flag to set pause/unpause animation */
static const int animation_speed = 25; /**< animation delate in ms */
static const double step = 0.01; /**< animation step size */
static double l_ratio = step * 10; /**< the l-ratio defined in docs */
static double k_ratio = step; /**< the k-ratio defined in docs */
static const double num_rot = 20.; /**< number of rotations to simulate */
/** A wrapper that is not available in all GLUT implementations.
*/
static inline void glutBitmapString(void *font, char *message) {
for (char *ch = message; *ch != '\0'; ch++) glutBitmapCharacter(font, *ch);
}
/**
* @brief Function to graph (x,y) points on the OpenGL graphics window.
*
* @tparam N number of points = size of array
* @param [in] points Array of 2D points represented as std::pair
* @param l the relative distance of marker from the centre of
* inner circle and \f$0\le l\le1\f$ to display info
* @param k the ratio of radius of inner circle to outer circle and \f$0<k<1\f$
* to display info
*/
template <size_t N>
void display_graph(const std::array<std::pair<double, double>, N> &points,
double l, double k) {
glClearColor(1.0f, 1.0f, 1.0f,
0.0f); // Set background color to white and opaque
glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer (background)
glBegin(GL_LINES); // draw line segments
glColor3f(0.f, 0.f, 1.f); // blue
glPointSize(2.f); // point size in pixels
for (size_t i = 1; i < N; i++) {
glVertex2f(points[i - 1].first, points[i - 1].second); // line from
glVertex2f(points[i].first, points[i].second); // line to
}
glEnd();
glColor3f(0.f, 0.f, 0.f);
std::stringstream buffer;
buffer << std::setw(3) << "l = " << l;
glRasterPos2f(-.85, .85);
glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24,
const_cast<char *>(buffer.str().c_str()));
buffer.str("");
buffer.clear();
buffer << std::setw(3) << "k = " << k;
glRasterPos2f(-.85, .70);
glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24,
const_cast<char *>(buffer.str().c_str()));
glutSwapBuffers();
}
/**
* @brief Test function with animation
*
*/
void test2() {
const size_t N = 5000; // number of samples
static bool direction1 = true; // increment if true, otherwise decrement
static bool direction2 = true; // increment if true, otherwise decrement
std::array<std::pair<double, double>, N> points;
spirograph(&points, l_ratio, k_ratio, num_rot);
display_graph(points, l_ratio, k_ratio);
if (paused)
// if paused, do not update l_ratio and k_ratio
return;
if (direction1) { // increment k_ratio
if (k_ratio >= (1.f - step)) // maximum limit
direction1 = false; // reverse direction of k_ratio
else
k_ratio += step;
} else { // decrement k_ratio
if (k_ratio <= step) { // minimum limit
direction1 = true; // reverse direction of k_ratio
if (direction2) { // increment l_ratio
if (l_ratio >= (1.f - step)) // max limit of l_ratio
direction2 = false; // reverse direction of l_ratio
else
l_ratio += step;
} else { // decrement l_ratio
if (l_ratio <= step) // minimum limit of l_ratio
direction2 = true; // reverse direction of l_ratio
else
l_ratio -= step;
}
} else { // no min limit of k_ratio
k_ratio -= step;
}
}
}
/**
* @brief GLUT timer callback function to add animation delay.
*/
void timer_cb(int t) {
glutTimerFunc(animation_speed, timer_cb, 0);
glutPostRedisplay();
}
/**
* @brief Keypress event call back function.
*
* @param key ID of the key pressed
* @param x mouse pointer position at event
* @param y mouse pointer position at event
*/
void keyboard_cb(unsigned char key, int x, int y) {
switch (key) {
case ' ': // spacebar toggles pause
paused = !paused; // toggle
break;
case GLUT_KEY_UP:
case '+': // up arrow key
k_ratio += step;
break;
case GLUT_KEY_DOWN:
case '_': // down arrow key
k_ratio -= step;
break;
case GLUT_KEY_RIGHT:
case '=': // left arrow key
l_ratio += step;
break;
case GLUT_KEY_LEFT:
case '-': // right arrow key
l_ratio -= step;
break;
case 0x1B: // escape key exits
exit(EXIT_SUCCESS);
default:
return;
}
}
#endif
} // namespace spirograph
/** Main function */
int main(int argc, char **argv) {
spirograph::test();
#ifdef USE_GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutCreateWindow("Spirograph");
glutInitWindowSize(400, 400);
// glutIdleFunc(glutPostRedisplay);
glutTimerFunc(spirograph::animation_speed, spirograph::timer_cb, 0);
glutKeyboardFunc(spirograph::keyboard_cb);
glutDisplayFunc(spirograph::test2);
glutMainLoop();
#endif
return 0;
}

View File

@@ -7,10 +7,12 @@
*
* \author [Krishna Vedala](https://github.com/kvedala)
*
* <img
* \details
* <a href="https://commons.wikimedia.org/wiki/File:Adaline_flow_chart.gif"><img
* src="https://upload.wikimedia.org/wikipedia/commons/b/be/Adaline_flow_chart.gif"
* width="200px">
* [source](https://commons.wikimedia.org/wiki/File:Adaline_flow_chart.gif)
* alt="Structure of an ADALINE network. Source: Wikipedia"
* style="width:200px; float:right;"></a>
*
* ADALINE is one of the first and simplest single layer artificial neural
* network. The algorithm essentially implements a linear function
* \f[ f\left(x_0,x_1,x_2,\ldots\right) =
@@ -24,6 +26,7 @@
* computed using stochastic gradient descent method.
*/
#include <array>
#include <cassert>
#include <climits>
#include <cmath>
@@ -33,7 +36,8 @@
#include <numeric>
#include <vector>
#define MAX_ITER 500 // INT_MAX ///< Maximum number of iterations to learn
/** Maximum number of iterations to learn */
constexpr int MAX_ITER = 500; // INT_MAX
/** \namespace machine_learning
* \brief Machine learning algorithms
@@ -48,8 +52,8 @@ class adaline {
* \param[in] convergence accuracy (optional,
* default=\f$1\times10^{-5}\f$)
*/
adaline(int num_features, const double eta = 0.01f,
const double accuracy = 1e-5)
explicit adaline(int num_features, const double eta = 0.01f,
const double accuracy = 1e-5)
: eta(eta), accuracy(accuracy) {
if (eta <= 0) {
std::cerr << "learning rate should be positive and nonzero"
@@ -62,7 +66,7 @@ class adaline {
1); // additional weight is for the constant bias term
// initialize with random weights in the range [-50, 49]
for (int i = 0; i < weights.size(); i++) weights[i] = 1.f;
for (double &weight : weights) weight = 1.f;
// weights[i] = (static_cast<double>(std::rand() % 100) - 50);
}
@@ -73,8 +77,9 @@ class adaline {
out << "<";
for (int i = 0; i < ada.weights.size(); i++) {
out << ada.weights[i];
if (i < ada.weights.size() - 1)
if (i < ada.weights.size() - 1) {
out << ", ";
}
}
out << ">";
return out;
@@ -88,28 +93,33 @@ class adaline {
* model prediction output
*/
int predict(const std::vector<double> &x, double *out = nullptr) {
if (!check_size_match(x))
if (!check_size_match(x)) {
return 0;
}
double y = weights.back(); // assign bias value
// for (int i = 0; i < x.size(); i++) y += x[i] * weights[i];
y = std::inner_product(x.begin(), x.end(), weights.begin(), y);
if (out != nullptr) // if out variable is provided
if (out != nullptr) { // if out variable is provided
*out = y;
}
return activation(y); // quantizer: apply ADALINE threshold function
}
/**
* Update the weights of the model using supervised learning for one
* feature vector \param[in] x feature vector \param[in] y known output
* value \returns correction factor
* feature vector
* \param[in] x feature vector
* \param[in] y known output value
* \returns correction factor
*/
double fit(const std::vector<double> &x, const int &y) {
if (!check_size_match(x))
if (!check_size_match(x)) {
return 0;
}
/* output of the model with current weights */
int p = predict(x);
@@ -127,21 +137,23 @@ class adaline {
/**
* Update the weights of the model using supervised learning for an
* array of vectors. \param[in] X array of feature vector \param[in] y
* known output value for each feature vector
* array of vectors.
* \param[in] X array of feature vector
* \param[in] y known output value for each feature vector
*/
template <int N>
void fit(std::vector<double> const (&X)[N], const int *y) {
template <size_t N>
void fit(std::array<std::vector<double>, N> const &X,
std::array<int, N> const &Y) {
double avg_pred_error = 1.f;
int iter;
int iter = 0;
for (iter = 0; (iter < MAX_ITER) && (avg_pred_error > accuracy);
iter++) {
avg_pred_error = 0.f;
// perform fit for each sample
for (int i = 0; i < N; i++) {
double err = fit(X[i], y[i]);
double err = fit(X[i], Y[i]);
avg_pred_error += std::abs(err);
}
avg_pred_error /= N;
@@ -152,15 +164,25 @@ class adaline {
<< "\tAvg error: " << avg_pred_error << std::endl;
}
if (iter < MAX_ITER)
if (iter < MAX_ITER) {
std::cout << "Converged after " << iter << " iterations."
<< std::endl;
else
} else {
std::cout << "Did not converge after " << iter << " iterations."
<< std::endl;
}
}
/** Defines activation function as Heaviside's step function.
* \f[
* f(x) = \begin{cases}
* -1 & \forall x \le 0\\
* 1 & \forall x > 0
* \end{cases}
* \f]
* @param x input value to apply activation on
* @return activation output
*/
int activation(double x) { return x > 0 ? 1 : -1; }
private:
@@ -204,15 +226,19 @@ void test1(double eta = 0.01) {
const int N = 10; // number of sample points
std::vector<double> X[N] = {{0, 1}, {1, -2}, {2, 3}, {3, -1},
{4, 1}, {6, -5}, {-7, -3}, {-8, 5},
{-9, 2}, {-10, -15}};
int y[] = {1, -1, 1, -1, -1, -1, 1, 1, 1, -1}; // corresponding y-values
std::array<std::vector<double>, N> X = {
std::vector<double>({0, 1}), std::vector<double>({1, -2}),
std::vector<double>({2, 3}), std::vector<double>({3, -1}),
std::vector<double>({4, 1}), std::vector<double>({6, -5}),
std::vector<double>({-7, -3}), std::vector<double>({-8, 5}),
std::vector<double>({-9, 2}), std::vector<double>({-10, -15})};
std::array<int, N> y = {1, -1, 1, -1, -1,
-1, 1, 1, 1, -1}; // corresponding y-values
std::cout << "------- Test 1 -------" << std::endl;
std::cout << "Model before fit: " << ada << std::endl;
ada.fit(X, y);
ada.fit<N>(X, y);
std::cout << "Model after fit: " << ada << std::endl;
int predict = ada.predict({5, -3});
@@ -238,17 +264,17 @@ void test2(double eta = 0.01) {
const int N = 50; // number of sample points
std::vector<double> X[N];
int Y[N]; // corresponding y-values
std::array<std::vector<double>, N> X;
std::array<int, N> Y{}; // corresponding y-values
// generate sample points in the interval
// [-range2/100 , (range2-1)/100]
int range = 500; // sample points full-range
int range2 = range >> 1; // sample points half-range
for (int i = 0; i < N; i++) {
double x0 = ((std::rand() % range) - range2) / 100.f;
double x1 = ((std::rand() % range) - range2) / 100.f;
X[i] = {x0, x1};
double x0 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
double x1 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
X[i] = std::vector<double>({x0, x1});
Y[i] = (x0 + 3. * x1) > -1 ? 1 : -1;
}
@@ -260,8 +286,8 @@ void test2(double eta = 0.01) {
int N_test_cases = 5;
for (int i = 0; i < N_test_cases; i++) {
double x0 = ((std::rand() % range) - range2) / 100.f;
double x1 = ((std::rand() % range) - range2) / 100.f;
double x0 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
double x1 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
int predict = ada.predict({x0, x1});
@@ -289,18 +315,18 @@ void test3(double eta = 0.01) {
const int N = 100; // number of sample points
std::vector<double> X[N];
int Y[N]; // corresponding y-values
std::array<std::vector<double>, N> X;
std::array<int, N> Y{}; // corresponding y-values
// generate sample points in the interval
// [-range2/100 , (range2-1)/100]
int range = 200; // sample points full-range
int range2 = range >> 1; // sample points half-range
for (int i = 0; i < N; i++) {
double x0 = ((std::rand() % range) - range2) / 100.f;
double x1 = ((std::rand() % range) - range2) / 100.f;
double x2 = ((std::rand() % range) - range2) / 100.f;
X[i] = {x0, x1, x2, x0 * x0, x1 * x1, x2 * x2};
double x0 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
double x1 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
double x2 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
X[i] = std::vector<double>({x0, x1, x2, x0 * x0, x1 * x1, x2 * x2});
Y[i] = ((x0 * x0) + (x1 * x1) + (x2 * x2)) <= 1.f ? 1 : -1;
}
@@ -312,9 +338,9 @@ void test3(double eta = 0.01) {
int N_test_cases = 5;
for (int i = 0; i < N_test_cases; i++) {
double x0 = ((std::rand() % range) - range2) / 100.f;
double x1 = ((std::rand() % range) - range2) / 100.f;
double x2 = ((std::rand() % range) - range2) / 100.f;
double x0 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
double x1 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
double x2 = (static_cast<double>(std::rand() % range) - range2) / 100.f;
int predict = ada.predict({x0, x1, x2, x0 * x0, x1 * x1, x2 * x2});
@@ -332,8 +358,9 @@ int main(int argc, char **argv) {
std::srand(std::time(nullptr)); // initialize random number generator
double eta = 0.1; // default value of eta
if (argc == 2) // read eta value from commandline argument if present
if (argc == 2) { // read eta value from commandline argument if present
eta = strtof(argv[1], nullptr);
}
test1(eta);

View File

@@ -3,9 +3,11 @@
* @{
* \file
* \author [Krishna Vedala](https://github.com/kvedala)
*
* \brief [Kohonen self organizing
* map](https://en.wikipedia.org/wiki/Self-organizing_map) (topological map)
*
* \details
* This example implements a powerful unsupervised learning algorithm called as
* a self organizing map. The algorithm creates a connected network of weights
* that closely follows the given data points. This thus creates a topological
@@ -21,10 +23,13 @@
* than with GCC on windows
* \see kohonen_som_trace.cpp
*/
#define _USE_MATH_DEFINES // required for MS Visual C++
#define _USE_MATH_DEFINES //< required for MS Visual C++
#include <algorithm>
#include <array>
#include <cerrno>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iostream>
@@ -66,7 +71,8 @@ int save_2d_data(const char *fname,
fp.open(fname);
if (!fp.is_open()) {
// error with opening file to write
std::cerr << "Error opening file " << fname << "\n";
std::cerr << "Error opening file " << fname << ": "
<< std::strerror(errno) << "\n";
return -1;
}
@@ -74,12 +80,14 @@ int save_2d_data(const char *fname,
for (int i = 0; i < num_points; i++) {
// for each feature in the array
for (int j = 0; j < num_features; j++) {
fp << X[i][j]; // print the feature value
if (j < num_features - 1) // if not the last feature
fp << ","; // suffix comma
fp << X[i][j]; // print the feature value
if (j < num_features - 1) { // if not the last feature
fp << ","; // suffix comma
}
}
if (i < num_points - 1) { // if not the last row
fp << "\n"; // start a new line
}
if (i < num_points - 1) // if not the last row
fp << "\n"; // start a new line
}
fp.close();
@@ -97,12 +105,12 @@ int save_2d_data(const char *fname,
void get_min_2d(const std::vector<std::valarray<double>> &X, double *val,
int *x_idx, int *y_idx) {
val[0] = INFINITY; // initial min value
int N = X.size();
size_t N = X.size();
for (int i = 0; i < N; i++) { // traverse each x-index
auto result = std::min_element(std::begin(X[i]), std::end(X[i]));
double d_min = *result;
int j = std::distance(std::begin(X[i]), result);
std::ptrdiff_t j = std::distance(std::begin(X[i]), result);
if (d_min < val[0]) { // if a lower value is found
// save the value and its index
@@ -117,7 +125,8 @@ void get_min_2d(const std::vector<std::valarray<double>> &X, double *val,
* \brief Machine learning algorithms
*/
namespace machine_learning {
#define MIN_DISTANCE 1e-4 ///< Minimum average distance of image nodes
/** Minimum average distance of image nodes */
constexpr double MIN_DISTANCE = 1e-4;
/**
* Create the distance matrix or
@@ -134,9 +143,8 @@ int save_u_matrix(const char *fname,
const std::vector<std::vector<std::valarray<double>>> &W) {
std::ofstream fp(fname);
if (!fp) { // error with fopen
char msg[120];
std::snprintf(msg, sizeof(msg), "File error (%s): ", fname);
std::perror(msg);
std::cerr << "File error (" << fname << "): " << std::strerror(errno)
<< std::endl;
return -1;
}
@@ -151,7 +159,7 @@ int save_u_matrix(const char *fname,
int to_x = std::min<int>(W.size(), i + R + 1);
int from_y = std::max<int>(0, j - R);
int to_y = std::min<int>(W[0].size(), j + R + 1);
int l, m;
int l = 0, m = 0;
#ifdef _OPENMP
#pragma omp parallel for reduction(+ : distance)
#endif
@@ -170,8 +178,9 @@ int save_u_matrix(const char *fname,
fp << ','; // suffix comma
}
}
if (i < W.size() - 1) // if not the last row
fp << '\n'; // start a new line
if (i < W.size() - 1) { // if not the last row
fp << '\n'; // start a new line
}
}
fp.close();
@@ -192,10 +201,11 @@ double update_weights(const std::valarray<double> &X,
std::vector<std::vector<std::valarray<double>>> *W,
std::vector<std::valarray<double>> *D, double alpha,
int R) {
int x, y;
int x = 0, y = 0;
int num_out_x = static_cast<int>(W->size()); // output nodes - in X
int num_out_y = static_cast<int>(W[0][0].size()); // output nodes - in Y
int num_features = static_cast<int>(W[0][0][0].size()); // features = in Z
// int num_features = static_cast<int>(W[0][0][0].size()); // features =
// in Z
double d_min = 0.f;
#ifdef _OPENMP
@@ -215,7 +225,7 @@ double update_weights(const std::valarray<double> &X,
// step 2: get closest node i.e., node with snallest Euclidian distance
// to the current pattern
int d_min_x, d_min_y;
int d_min_x = 0, d_min_y = 0;
get_min_2d(*D, &d_min, &d_min_x, &d_min_y);
// step 3a: get the neighborhood range
@@ -259,10 +269,10 @@ double update_weights(const std::valarray<double> &X,
void kohonen_som(const std::vector<std::valarray<double>> &X,
std::vector<std::vector<std::valarray<double>>> *W,
double alpha_min) {
int num_samples = X.size(); // number of rows
int num_features = X[0].size(); // number of columns
int num_out = W->size(); // output matrix size
int R = num_out >> 2, iter = 0;
size_t num_samples = X.size(); // number of rows
// size_t num_features = X[0].size(); // number of columns
size_t num_out = W->size(); // output matrix size
size_t R = num_out >> 2, iter = 0;
double alpha = 1.f;
std::vector<std::valarray<double>> D(num_out);
@@ -281,15 +291,17 @@ void kohonen_som(const std::vector<std::valarray<double>> &X,
}
// every 100th iteration, reduce the neighborhood range
if (iter % 300 == 0 && R > 1)
if (iter % 300 == 0 && R > 1) {
R--;
}
dmin /= num_samples;
// termination condition variable -> % change in minimum distance
dmin_ratio = (past_dmin - dmin) / past_dmin;
if (dmin_ratio < 0)
if (dmin_ratio < 0) {
dmin_ratio = 1.f;
}
past_dmin = dmin;
std::cout << "iter: " << iter << "\t alpha: " << alpha << "\t R: " << R
@@ -318,14 +330,14 @@ using machine_learning::save_u_matrix;
void test_2d_classes(std::vector<std::valarray<double>> *data) {
const int N = data->size();
const double R = 0.3; // radius of cluster
int i;
int i = 0;
const int num_classes = 4;
const double centres[][2] = {
std::array<std::array<double, 2>, num_classes> centres = {
// centres of each class cluster
{.5, .5}, // centre of class 1
{.5, -.5}, // centre of class 2
{-.5, .5}, // centre of class 3
{-.5, -.5} // centre of class 4
std::array<double, 2>({.5, .5}), // centre of class 1
std::array<double, 2>({.5, -.5}), // centre of class 2
std::array<double, 2>({-.5, .5}), // centre of class 3
std::array<double, 2>({-.5, -.5}) // centre of class 4
};
#ifdef _OPENMP
@@ -355,15 +367,16 @@ void test_2d_classes(std::vector<std::valarray<double>> *data) {
* * `w12.csv`: trained SOM map
*/
void test1() {
int j, N = 300;
int j = 0, N = 300;
int features = 2;
int num_out = 30;
std::vector<std::valarray<double>> X(N);
std::vector<std::vector<std::valarray<double>>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) // only add new arrays if i < N
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::vector<std::valarray<double>>(num_out);
for (int k = 0; k < num_out; k++) {
@@ -371,9 +384,10 @@ void test1() {
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++)
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][k][j] = _random(-10, 10);
}
}
}
}
@@ -395,16 +409,16 @@ void test1() {
* \param[out] data matrix to store data in
*/
void test_3d_classes1(std::vector<std::valarray<double>> *data) {
const int N = data->size();
const size_t N = data->size();
const double R = 0.3; // radius of cluster
int i;
int i = 0;
const int num_classes = 4;
const double centres[][3] = {
const std::array<std::array<double, 3>, num_classes> centres = {
// centres of each class cluster
{.5, .5, .5}, // centre of class 1
{.5, -.5, -.5}, // centre of class 2
{-.5, .5, .5}, // centre of class 3
{-.5, -.5 - .5} // centre of class 4
std::array<double, 3>({.5, .5, .5}), // centre of class 1
std::array<double, 3>({.5, -.5, -.5}), // centre of class 2
std::array<double, 3>({-.5, .5, .5}), // centre of class 3
std::array<double, 3>({-.5, -.5 - .5}) // centre of class 4
};
#ifdef _OPENMP
@@ -435,15 +449,16 @@ void test_3d_classes1(std::vector<std::valarray<double>> *data) {
* * `w22.csv`: trained SOM map
*/
void test2() {
int j, N = 300;
int j = 0, N = 300;
int features = 3;
int num_out = 30;
std::vector<std::valarray<double>> X(N);
std::vector<std::vector<std::valarray<double>>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) // only add new arrays if i < N
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::vector<std::valarray<double>>(num_out);
for (int k = 0; k < num_out; k++) {
@@ -451,9 +466,10 @@ void test2() {
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++)
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][k][j] = _random(-10, 10);
}
}
}
}
@@ -475,20 +491,20 @@ void test2() {
* \param[out] data matrix to store data in
*/
void test_3d_classes2(std::vector<std::valarray<double>> *data) {
const int N = data->size();
const size_t N = data->size();
const double R = 0.2; // radius of cluster
int i;
int i = 0;
const int num_classes = 8;
const double centres[][3] = {
const std::array<std::array<double, 3>, num_classes> centres = {
// centres of each class cluster
{.5, .5, .5}, // centre of class 1
{.5, .5, -.5}, // centre of class 2
{.5, -.5, .5}, // centre of class 3
{.5, -.5, -.5}, // centre of class 4
{-.5, .5, .5}, // centre of class 5
{-.5, .5, -.5}, // centre of class 6
{-.5, -.5, .5}, // centre of class 7
{-.5, -.5, -.5} // centre of class 8
std::array<double, 3>({.5, .5, .5}), // centre of class 1
std::array<double, 3>({.5, .5, -.5}), // centre of class 2
std::array<double, 3>({.5, -.5, .5}), // centre of class 3
std::array<double, 3>({.5, -.5, -.5}), // centre of class 4
std::array<double, 3>({-.5, .5, .5}), // centre of class 5
std::array<double, 3>({-.5, .5, -.5}), // centre of class 6
std::array<double, 3>({-.5, -.5, .5}), // centre of class 7
std::array<double, 3>({-.5, -.5, -.5}) // centre of class 8
};
#ifdef _OPENMP
@@ -519,15 +535,16 @@ void test_3d_classes2(std::vector<std::valarray<double>> *data) {
* * `w32.csv`: trained SOM map
*/
void test3() {
int j, N = 500;
int j = 0, N = 500;
int features = 3;
int num_out = 30;
std::vector<std::valarray<double>> X(N);
std::vector<std::vector<std::valarray<double>>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) // only add new arrays if i < N
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::vector<std::valarray<double>>(num_out);
for (int k = 0; k < num_out; k++) {
@@ -535,9 +552,10 @@ void test3() {
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++)
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][k][j] = _random(-10, 10);
}
}
}
}

View File

@@ -20,6 +20,7 @@
*/
#define _USE_MATH_DEFINES // required for MS Visual C++
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdlib>
#include <ctime>
@@ -71,12 +72,14 @@ int save_nd_data(const char *fname,
for (int i = 0; i < num_points; i++) {
// for each feature in the array
for (int j = 0; j < num_features; j++) {
fp << X[i][j]; // print the feature value
if (j < num_features - 1) // if not the last feature
fp << ","; // suffix comma
fp << X[i][j]; // print the feature value
if (j < num_features - 1) { // if not the last feature
fp << ","; // suffix comma
}
}
if (i < num_points - 1) { // if not the last row
fp << "\n"; // start a new line
}
if (i < num_points - 1) // if not the last row
fp << "\n"; // start a new line
}
fp.close();
@@ -100,9 +103,9 @@ namespace machine_learning {
void update_weights(const std::valarray<double> &x,
std::vector<std::valarray<double>> *W,
std::valarray<double> *D, double alpha, int R) {
int j, k;
int num_out = W->size(); // number of SOM output nodes
int num_features = x.size(); // number of data features
int j = 0, k = 0;
int num_out = W->size(); // number of SOM output nodes
// int num_features = x.size(); // number of data features
#ifdef _OPENMP
#pragma omp for
@@ -117,7 +120,7 @@ void update_weights(const std::valarray<double> &x,
// step 2: get closest node i.e., node with snallest Euclidian distance to
// the current pattern
auto result = std::min_element(std::begin(*D), std::end(*D));
double d_min = *result;
// double d_min = *result;
int d_min_idx = std::distance(std::begin(*D), result);
// step 3a: get the neighborhood range
@@ -129,9 +132,10 @@ void update_weights(const std::valarray<double> &x,
#ifdef _OPENMP
#pragma omp for
#endif
for (j = from_node; j < to_node; j++)
for (j = from_node; j < to_node; j++) {
// update weights of nodes in the neighborhood
(*W)[j] += alpha * (x - (*W)[j]);
}
}
/**
@@ -145,16 +149,16 @@ void update_weights(const std::valarray<double> &x,
void kohonen_som_tracer(const std::vector<std::valarray<double>> &X,
std::vector<std::valarray<double>> *W,
double alpha_min) {
int num_samples = X.size(); // number of rows
int num_features = X[0].size(); // number of columns
int num_out = W->size(); // number of rows
int num_samples = X.size(); // number of rows
// int num_features = X[0].size(); // number of columns
int num_out = W->size(); // number of rows
int R = num_out >> 2, iter = 0;
double alpha = 1.f;
std::valarray<double> D(num_out);
// Loop alpha from 1 to slpha_min
for (; alpha > alpha_min; alpha -= 0.01, iter++) {
do {
// Loop for each sample pattern in the data set
for (int sample = 0; sample < num_samples; sample++) {
// update weights for the current input pattern sample
@@ -162,9 +166,13 @@ void kohonen_som_tracer(const std::vector<std::valarray<double>> &X,
}
// every 10th iteration, reduce the neighborhood range
if (iter % 10 == 0 && R > 1)
if (iter % 10 == 0 && R > 1) {
R--;
}
}
alpha -= 0.01;
iter++;
} while (alpha > alpha_min);
}
} // namespace machine_learning
@@ -190,7 +198,7 @@ void test_circle(std::vector<std::valarray<double>> *data) {
const double R = 0.75, dr = 0.3;
double a_t = 0., b_t = 2.f * M_PI; // theta random between 0 and 2*pi
double a_r = R - dr, b_r = R + dr; // radius random between R-dr and R+dr
int i;
int i = 0;
#ifdef _OPENMP
#pragma omp for
@@ -223,24 +231,26 @@ void test_circle(std::vector<std::valarray<double>> *data) {
* output](https://raw.githubusercontent.com/TheAlgorithms/C-Plus-Plus/docs/images/machine_learning/kohonen/test1.svg)
*/
void test1() {
int j, N = 500;
int j = 0, N = 500;
int features = 2;
int num_out = 50;
std::vector<std::valarray<double>> X(N);
std::vector<std::valarray<double>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) // only add new arrays if i < N
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::valarray<double>(features);
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++)
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][j] = _random(-1, 1);
}
}
}
@@ -267,7 +277,7 @@ void test1() {
void test_lamniscate(std::vector<std::valarray<double>> *data) {
const int N = data->size();
const double dr = 0.2;
int i;
int i = 0;
#ifdef _OPENMP
#pragma omp for
@@ -303,24 +313,26 @@ void test_lamniscate(std::vector<std::valarray<double>> *data) {
* output](https://raw.githubusercontent.com/TheAlgorithms/C-Plus-Plus/docs/images/machine_learning/kohonen/test2.svg)
*/
void test2() {
int j, N = 500;
int j = 0, N = 500;
int features = 2;
int num_out = 20;
std::vector<std::valarray<double>> X(N);
std::vector<std::valarray<double>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) // only add new arrays if i < N
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::valarray<double>(features);
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++)
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][j] = _random(-1, 1);
}
}
}
@@ -347,18 +359,18 @@ void test2() {
void test_3d_classes(std::vector<std::valarray<double>> *data) {
const int N = data->size();
const double R = 0.1; // radius of cluster
int i;
int i = 0;
const int num_classes = 8;
const double centres[][3] = {
const std::array<const std::array<double, 3>, num_classes> centres = {
// centres of each class cluster
{.5, .5, .5}, // centre of class 0
{.5, .5, -.5}, // centre of class 1
{.5, -.5, .5}, // centre of class 2
{.5, -.5, -.5}, // centre of class 3
{-.5, .5, .5}, // centre of class 4
{-.5, .5, -.5}, // centre of class 5
{-.5, -.5, .5}, // centre of class 6
{-.5, -.5, -.5} // centre of class 7
std::array<double, 3>({.5, .5, .5}), // centre of class 0
std::array<double, 3>({.5, .5, -.5}), // centre of class 1
std::array<double, 3>({.5, -.5, .5}), // centre of class 2
std::array<double, 3>({.5, -.5, -.5}), // centre of class 3
std::array<double, 3>({-.5, .5, .5}), // centre of class 4
std::array<double, 3>({-.5, .5, -.5}), // centre of class 5
std::array<double, 3>({-.5, -.5, .5}), // centre of class 6
std::array<double, 3>({-.5, -.5, -.5}) // centre of class 7
};
#ifdef _OPENMP
@@ -400,24 +412,26 @@ void test_3d_classes(std::vector<std::valarray<double>> *data) {
* output](https://raw.githubusercontent.com/TheAlgorithms/C-Plus-Plus/docs/images/machine_learning/kohonen/test3.svg)
*/
void test3() {
int j, N = 200;
int j = 0, N = 200;
int features = 3;
int num_out = 20;
std::vector<std::valarray<double>> X(N);
std::vector<std::valarray<double>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) // only add new arrays if i < N
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::valarray<double>(features);
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++)
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][j] = _random(-1, 1);
}
}
}

View File

@@ -3,11 +3,14 @@
* \brief Linear regression example using [Ordinary least
* squares](https://en.wikipedia.org/wiki/Ordinary_least_squares)
*
* \author [Krishna Vedala](https://github.com/kvedala)
* Program that gets the number of data samples and number of features per
* sample along with output per sample. It applies OLS regression to compute
* the regression output for additional test data samples.
*
* \author [Krishna Vedala](https://github.com/kvedala)
*/
#include <cassert>
#include <cmath> // for std::abs
#include <iomanip> // for print formatting
#include <iostream>
#include <vector>
@@ -22,9 +25,10 @@ std::ostream &operator<<(std::ostream &out,
const char separator = ' ';
for (size_t row = 0; row < v.size(); row++) {
for (size_t col = 0; col < v[row].size(); col++)
for (size_t col = 0; col < v[row].size(); col++) {
out << std::left << std::setw(width) << std::setfill(separator)
<< v[row][col];
}
out << std::endl;
}
@@ -39,9 +43,10 @@ std::ostream &operator<<(std::ostream &out, std::vector<T> const &v) {
const int width = 15;
const char separator = ' ';
for (size_t row = 0; row < v.size(); row++)
for (size_t row = 0; row < v.size(); row++) {
out << std::left << std::setw(width) << std::setfill(separator)
<< v[row];
}
return out;
}
@@ -54,9 +59,11 @@ template <typename T>
inline bool is_square(std::vector<std::vector<T>> const &A) {
// Assuming A is square matrix
size_t N = A.size();
for (size_t i = 0; i < N; i++)
if (A[i].size() != N)
for (size_t i = 0; i < N; i++) {
if (A[i].size() != N) {
return false;
}
}
return true;
}
@@ -87,8 +94,9 @@ std::vector<std::vector<T>> operator*(std::vector<std::vector<T>> const &A,
std::vector<T> v(N_B);
for (size_t col = 0; col < N_B; col++) {
v[col] = static_cast<T>(0);
for (size_t j = 0; j < B.size(); j++)
for (size_t j = 0; j < B.size(); j++) {
v[col] += A[row][j] * B[j][col];
}
}
result[row] = v;
}
@@ -151,8 +159,9 @@ std::vector<float> operator*(std::vector<T> const &A, float const scalar) {
std::vector<float> result(N_A);
for (size_t row = 0; row < N_A; row++)
for (size_t row = 0; row < N_A; row++) {
result[row] = A[row] * static_cast<float>(scalar);
}
return result;
}
@@ -223,8 +232,9 @@ std::vector<std::vector<float>> get_inverse(
for (size_t row = 0; row < N; row++) {
// preallocatae a resultant identity matrix
inverse[row] = std::vector<float>(N);
for (size_t col = 0; col < N; col++)
for (size_t col = 0; col < N; col++) {
inverse[row][col] = (row == col) ? 1.f : 0.f;
}
}
if (!is_square(A)) {
@@ -236,8 +246,9 @@ std::vector<std::vector<float>> get_inverse(
std::vector<std::vector<float>> temp(N);
for (size_t row = 0; row < N; row++) {
std::vector<float> v(N);
for (size_t col = 0; col < N; col++)
for (size_t col = 0; col < N; col++) {
v[col] = static_cast<float>(A[row][col]);
}
temp[row] = v;
}
@@ -264,13 +275,14 @@ std::vector<std::vector<float>> get_inverse(
}
// set diagonal to 1
float divisor = static_cast<float>(temp[row][row]);
auto divisor = static_cast<float>(temp[row][row]);
temp[row] = temp[row] / divisor;
inverse[row] = inverse[row] / divisor;
// Row transformations
for (size_t row2 = 0; row2 < N; row2++) {
if (row2 == row)
if (row2 == row) {
continue;
}
float factor = temp[row2][row];
temp[row2] = temp[row2] - factor * temp[row];
inverse[row2] = inverse[row2] - factor * inverse[row];
@@ -310,9 +322,10 @@ std::vector<float> fit_OLS_regressor(std::vector<std::vector<T>> const &X,
std::vector<T> const &Y) {
// NxF
std::vector<std::vector<T>> X2 = X;
for (size_t i = 0; i < X2.size(); i++)
for (size_t i = 0; i < X2.size(); i++) {
// add Y-intercept -> Nx(F+1)
X2[i].push_back(1);
}
// (F+1)xN
std::vector<std::vector<T>> Xt = get_transpose(X2);
// (F+1)x(F+1)
@@ -344,18 +357,73 @@ std::vector<float> predict_OLS_regressor(std::vector<std::vector<T>> const &X,
for (size_t rows = 0; rows < X.size(); rows++) {
// -> start with constant term
result[rows] = beta[X[0].size()];
for (size_t cols = 0; cols < X[0].size(); cols++)
for (size_t cols = 0; cols < X[0].size(); cols++) {
result[rows] += beta[cols] * X[rows][cols];
}
}
// Nx1
return result;
}
/** Self test checks */
void ols_test() {
int F = 3, N = 5;
/* test function = x^2 -5 */
std::cout << "Test 1 (quadratic function)....";
// create training data set with features = x, x^2, x^3
std::vector<std::vector<float>> data1(
{{-5, 25, -125}, {-1, 1, -1}, {0, 0, 0}, {1, 1, 1}, {6, 36, 216}});
// create corresponding outputs
std::vector<float> Y1({20, -4, -5, -4, 31});
// perform regression modelling
std::vector<float> beta1 = fit_OLS_regressor(data1, Y1);
// create test data set with same features = x, x^2, x^3
std::vector<std::vector<float>> test_data1(
{{-2, 4, -8}, {2, 4, 8}, {-10, 100, -1000}, {10, 100, 1000}});
// expected regression outputs
std::vector<float> expected1({-1, -1, 95, 95});
// predicted regression outputs
std::vector<float> out1 = predict_OLS_regressor(test_data1, beta1);
// compare predicted results are within +-0.01 limit of expected
for (size_t rows = 0; rows < out1.size(); rows++) {
assert(std::abs(out1[rows] - expected1[rows]) < 0.01);
}
std::cout << "passed\n";
/* test function = x^3 + x^2 - 100 */
std::cout << "Test 2 (cubic function)....";
// create training data set with features = x, x^2, x^3
std::vector<std::vector<float>> data2(
{{-5, 25, -125}, {-1, 1, -1}, {0, 0, 0}, {1, 1, 1}, {6, 36, 216}});
// create corresponding outputs
std::vector<float> Y2({-200, -100, -100, 98, 152});
// perform regression modelling
std::vector<float> beta2 = fit_OLS_regressor(data2, Y2);
// create test data set with same features = x, x^2, x^3
std::vector<std::vector<float>> test_data2(
{{-2, 4, -8}, {2, 4, 8}, {-10, 100, -1000}, {10, 100, 1000}});
// expected regression outputs
std::vector<float> expected2({-104, -88, -1000, 1000});
// predicted regression outputs
std::vector<float> out2 = predict_OLS_regressor(test_data2, beta2);
// compare predicted results are within +-0.01 limit of expected
for (size_t rows = 0; rows < out2.size(); rows++) {
assert(std::abs(out2[rows] - expected2[rows]) < 0.01);
}
std::cout << "passed\n";
std::cout << std::endl; // ensure test results are displayed on screen
// (flush stdout)
}
/**
* main function
*/
int main() {
size_t N, F;
ols_test();
size_t N = 0, F = 0;
std::cout << "Enter number of features: ";
// number of features = columns
@@ -368,15 +436,16 @@ int main() {
std::vector<float> Y(N);
std::cout
<< "Enter training data. Per sample, provide features ad one output."
<< "Enter training data. Per sample, provide features and one output."
<< std::endl;
for (size_t rows = 0; rows < N; rows++) {
std::vector<float> v(F);
std::cout << "Sample# " << rows + 1 << ": ";
for (size_t cols = 0; cols < F; cols++)
for (size_t cols = 0; cols < F; cols++) {
// get the F features
std::cin >> v[cols];
}
data[rows] = v;
// get the corresponding output
std::cin >> Y[rows];
@@ -385,7 +454,7 @@ int main() {
std::vector<float> beta = fit_OLS_regressor(data, Y);
std::cout << std::endl << std::endl << "beta:" << beta << std::endl;
size_t T;
size_t T = 0;
std::cout << "Enter number of test samples: ";
// number of test sample inputs
std::cin >> T;

80
math/armstrong_number.cpp Normal file
View File

@@ -0,0 +1,80 @@
/**
* @file
* \brief Program to check if a number is an [Armstrong/Narcissistic
* number](https://en.wikipedia.org/wiki/Narcissistic_number) in decimal system.
*
* \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
*/
#include <cassert>
#include <cmath>
#include <iostream>
/**
* Function to calculate the total number of digits in the number.
* @param num Number
* @return Total number of digits.
*/
int number_of_digits(int num) {
int total_digits = 0;
while (num > 0) {
num = num / 10;
++total_digits;
}
return total_digits;
}
/**
* Function to check whether the number is armstrong number or not.
* @param num Number
* @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 (number < 0) {
return false;
}
int sum = 0;
int temp = number;
// Finding the total number of digits in the number
int total_digits = number_of_digits(number);
while (temp > 0) {
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);
temp = temp / 10;
}
return number == sum;
}
/**
* Function for testing the is_armstrong() function
* with all the test cases.
*/
void test() {
// is_armstrong(370) returns true.
assert(is_armstrong(370) == true);
// is_armstrong(225) returns false.
assert(is_armstrong(225) == false);
// is_armstrong(-23) returns false.
assert(is_armstrong(-23) == false);
// is_armstrong(153) returns true.
assert(is_armstrong(153) == true);
// is_armstrong(0) returns true.
assert(is_armstrong(0) == true);
// is_armstrong(12) returns false.
assert(is_armstrong(12) == false);
}
/**
* Main Function
*/
int main() {
test();
return 0;
}

View File

@@ -0,0 +1,72 @@
/**
*
* @file
* \brief A C++ Program to check whether a pair of number is [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
*/
#include <cassert>
#include <iostream>
/**
* Function to calculate the sum of all the proper divisor
* of an integer.
* @param num First 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;
// 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) {
// Check 'div' is divisor of 'num'.
if (num % div == 0) {
// If both divisor are same, add once to 'sum'
if (div == (num / div)) {
sum += div;
} else {
// If both divisor are not the same, add both to 'sum'.
sum += (div + (num / div));
}
}
}
return sum + 1;
}
/**
* 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
* @return `false` if the pair is not amicable
*/
bool are_amicable(int x, int y) {
return (sum_of_divisor(x) == y) && (sum_of_divisor(y) == x);
}
/**
* Function for testing the is_amicable() with
* all the test cases.
*/
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);
}
/**
* Main Function
*/
int main() {
test();
std::cout << "Assertion Success." << std::endl;
return 0;
}

271
math/complex_numbers.cpp Normal file
View File

@@ -0,0 +1,271 @@
/**
* @author tjgurwara99
* @file
*
* \brief An implementation of Complex Number as Objects
* \details A basic implementation of Complex Number field as a class with
* operators overloaded to accommodate (mathematical) field operations.
*/
#include <cassert>
#include <cmath>
#include <complex>
#include <ctime>
#include <iostream>
#include <stdexcept>
/**
* \brief Class Complex to represent complex numbers as a field.
*/
class Complex {
// The real value of the complex number
double re;
// The imaginary value of the complex number
double im;
public:
/**
* \brief Complex Constructor which initialises our complex number.
* \details
* Complex Constructor which initialises the complex number which takes
* three arguments.
* @param x If the third parameter is 'true' then this x is the absolute
* value of the complex number, if the third parameter is 'false' then this
* x is the real value of the complex number (optional).
* @param y If the third parameter is 'true' then this y is the argument of
* the complex number, if the third parameter is 'false' then this y is the
* imaginary value of the complex number (optional).
* @param is_polar 'false' by default. If we want to initialise our complex
* number using polar form then set this to true, otherwise set it to false
* to use initialiser which initialises real and imaginary values using the
* first two parameters (optional).
*/
explicit Complex(double x = 0.f, double y = 0.f, bool is_polar = false) {
if (!is_polar) {
re = x;
im = y;
return;
}
re = x * std::cos(y);
im = x * std::sin(y);
}
/**
* \brief Copy Constructor
* @param other The other number to equate our number to.
*/
Complex(const Complex &other) : re(other.real()), im(other.imag()) {}
/**
* \brief Member function to get real value of our complex number.
* Member function (getter) to access the class' re value.
*/
double real() const { return this->re; }
/**
* \brief Member function to get imaginary value of our complex number.
* Member function (getter) to access the class' im value.
*/
double imag() const { return this->im; }
/**
* \brief Member function to give the modulus of our complex number.
* Member function to which gives the absolute value (modulus) of our
* complex number
* @return \f$ \sqrt{z \bar{z}} \f$ where \f$ z \f$ is our complex
* number.
*/
double abs() const {
return std::sqrt(this->re * this->re + this->im * this->im);
}
/**
* \brief Member function to give the argument of our complex number.
* @return Argument of our Complex number in radians.
*/
double arg() const { return std::atan2(this->im, this->re); }
/**
* \brief Operator overload of '+' on Complex class.
* Operator overload to be able to add two complex numbers.
* @param other The other number that is added to the current number.
* @return result current number plus other number
*/
Complex operator+(const Complex &other) {
Complex result(this->re + other.re, this->im + other.im);
return result;
}
/**
* \brief Operator overload of '-' on Complex class.
* Operator overload to be able to subtract two complex numbers.
* @param other The other number being subtracted from the current number.
* @return result current number subtract other number
*/
Complex operator-(const Complex &other) {
Complex result(this->re - other.re, this->im - other.im);
return result;
}
/**
* \brief Operator overload of '*' on Complex class.
* Operator overload to be able to multiple two complex numbers.
* @param other The other number to multiply the current number to.
* @return result current number times other number.
*/
Complex operator*(const Complex &other) {
Complex result(this->re * other.re - this->im * other.im,
this->re * other.im + this->im * other.re);
return result;
}
/**
* \brief Operator overload of '~' on Complex class.
* Operator overload of the BITWISE NOT which gives us the conjugate of our
* complex number. NOTE: This is overloading the BITWISE operator but its
* not a BITWISE operation in this definition.
* @return result The conjugate of our complex number.
*/
Complex operator~() const {
Complex result(this->re, -(this->im));
return result;
}
/**
* \brief Operator overload of '/' on Complex class.
* Operator overload to be able to divide two complex numbers. This function
* would throw an exception if the other number is zero.
* @param other The other number we divide our number by.
* @return result Current number divided by other number.
*/
Complex operator/(const Complex &other) {
Complex result = *this * ~other;
double denominator =
other.real() * other.real() + other.imag() * other.imag();
if (denominator != 0) {
result = Complex(result.real() / denominator,
result.imag() / denominator);
return result;
} else {
throw std::invalid_argument("Undefined Value");
}
}
/**
* \brief Operator overload of '=' on Complex class.
* Operator overload to be able to copy RHS instance of Complex to LHS
* instance of Complex
*/
const Complex &operator=(const Complex &other) {
this->re = other.real();
this->im = other.imag();
return *this;
}
};
/**
* \brief Operator overload of '==' on Complex class.
* Logical Equal overload for our Complex class.
* @param a Left hand side of our expression
* @param b Right hand side of our expression
* @return 'True' If real and imaginary parts of a and b are same
* @return 'False' Otherwise.
*/
bool operator==(const Complex &a, const Complex &b) {
return a.real() == b.real() && a.imag() == b.imag();
}
/**
* \brief Operator overload of '<<' of ostream for Complex class.
* Overloaded insersion operator to accommodate the printing of our complex
* number in their standard form.
* @param os The console stream
* @param num The complex number.
*/
std::ostream &operator<<(std::ostream &os, const Complex &num) {
os << "(" << num.real();
if (num.imag() < 0) {
os << " - " << -num.imag();
} else {
os << " + " << num.imag();
}
os << "i)";
return os;
}
/**
* \brief Function to get random numbers to generate our complex numbers for
* test
*/
double get_rand() { return (std::rand() % 100 - 50) / 100.f; }
/**
* Tests Function
*/
void tests() {
std::srand(std::time(nullptr));
double x1 = get_rand(), y1 = get_rand(), x2 = get_rand(), y2 = get_rand();
Complex num1(x1, y1), num2(x2, y2);
std::complex<double> cnum1(x1, y1), cnum2(x2, y2);
Complex result;
std::complex<double> expected;
// Test for addition
result = num1 + num2;
expected = cnum1 + cnum2;
assert(((void)"1 + 1i + 1 + 1i is equal to 2 + 2i but the addition doesn't "
"add up \n",
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "First test passes." << std::endl;
// Test for subtraction
result = num1 - num2;
expected = cnum1 - cnum2;
assert(((void)"1 + 1i - 1 - 1i is equal to 0 but the program says "
"otherwise. \n",
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "Second test passes." << std::endl;
// Test for multiplication
result = num1 * num2;
expected = cnum1 * cnum2;
assert(((void)"(1 + 1i) * (1 + 1i) is equal to 2i but the program says "
"otherwise. \n",
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "Third test passes." << std::endl;
// Test for division
result = num1 / num2;
expected = cnum1 / cnum2;
assert(((void)"(1 + 1i) / (1 + 1i) is equal to 1 but the program says "
"otherwise.\n",
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "Fourth test passes." << std::endl;
// Test for conjugates
result = ~num1;
expected = std::conj(cnum1);
assert(((void)"(1 + 1i) has a conjugate which is equal to (1 - 1i) but the "
"program says otherwise.\n",
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "Fifth test passes.\n";
// Test for Argument of our complex number
assert(((void)"(1 + 1i) has argument PI / 4 but the program differs from "
"the std::complex result.\n",
(num1.arg() == std::arg(cnum1))));
std::cout << "Sixth test passes.\n";
// Test for absolute value of our complex number
assert(((void)"(1 + 1i) has absolute value sqrt(2) but the program differs "
"from the std::complex result. \n",
(num1.abs() == std::abs(cnum1))));
std::cout << "Seventh test passes.\n";
}
/**
* Main function
*/
int main() {
tests();
return 0;
}

View File

@@ -1,8 +1,9 @@
/**
* @file
* @brief Compute double factorial: \f$n!!\f$
* @brief Compute [double
* factorial](https://en.wikipedia.org/wiki/Double_factorial): \f$n!!\f$
*
* Double factorial of a non-negative integer n, is defined as the product of
* Double factorial of a non-negative integer `n`, is defined as the product of
* all the integers from 1 to n that have the same parity (odd or even) as n.
* <br/>It is also called as semifactorial of a number and is denoted by
* \f$n!!\f$
@@ -32,10 +33,38 @@ uint64_t double_factorial_recursive(uint64_t n) {
return n * double_factorial_recursive(n - 2);
}
/// main function
int main() {
uint64_t n;
std::cin >> n;
assert(n >= 0);
std::cout << double_factorial_iterative(n);
/** Wrapper to run tests using both recursive and iterative implementations.
* The checks are only valid in debug builds due to the use of `assert()`
* statements.
* \param [in] n number to check double factorial for
* \param [in] expected expected result
*/
void test(uint64_t n, uint64_t expected) {
assert(double_factorial_iterative(n) == expected);
assert(double_factorial_recursive(n) == expected);
}
/**
* Test implementations
*/
void tests() {
std::cout << "Test 1:\t n=5\t...";
test(5, 15);
std::cout << "passed\n";
std::cout << "Test 2:\t n=15\t...";
test(15, 2027025);
std::cout << "passed\n";
std::cout << "Test 3:\t n=0\t...";
test(0, 1);
std::cout << "passed\n";
}
/**
* Main function
*/
int main() {
tests();
return 0;
}

View File

@@ -19,28 +19,32 @@
#include <cstdio>
#include <iostream>
/** maximum number that can be computed - The result after 93 cannot be stored
* in a `uint64_t` data type. */
const uint64_t MAX = 93;
/**
* maximum number that can be computed - The result after 93 cannot be stored
* in a `uint64_t` data type.
*/
/** Array of computed fibonacci numbers */
uint64_t f[MAX] = {0};
#define MAX 93
/** Algorithm */
uint64_t fib(uint64_t n) {
if (n == 0)
static uint64_t f1 = 1,
f2 = 1; // using static keyword will retain the values of
// f1 and f2 for the next function call.
if (n <= 2)
return f2;
if (n >= 93) {
std::cerr
<< "Cannot compute for n>93 due to limit of 64-bit integers\n";
return 0;
if (n == 1 || n == 2)
return (f[n] = 1);
}
if (f[n])
return f[n];
uint64_t temp = f2; // we do not need temp to be static
f2 += f1;
f1 = temp;
uint64_t k = (n % 2 != 0) ? (n + 1) / 2 : n / 2;
f[n] = (n % 2 != 0) ? (fib(k) * fib(k) + fib(k - 1) * fib(k - 1))
: (2 * fib(k - 1) + fib(k)) * fib(k);
return f[n];
return f2;
}
/** Main function */

View File

@@ -53,7 +53,7 @@ class large_number {
/**< initializer from a string */
explicit large_number(char const *number_str) {
for (size_t i = strlen(number_str); i > 0; i--) {
unsigned char a = number_str[i - 1] - '0';
char a = number_str[i - 1] - '0';
if (a >= 0 && a <= 9)
_digits.push_back(a);
}
@@ -127,7 +127,7 @@ class large_number {
/**
* Get number of digits in the number
**/
const size_t num_digits() const { return _digits.size(); }
size_t num_digits() const { return _digits.size(); }
/**
* operator over load to access the
@@ -245,7 +245,7 @@ class large_number {
/**
* returns i^th digit as an ASCII character
**/
const char digit_char(size_t i) const {
char digit_char(size_t i) const {
return _digits[num_digits() - i - 1] + '0';
}
@@ -264,7 +264,7 @@ class large_number {
size_t i;
uint64_t carry = 0, temp;
for (i = 0; i < this->num_digits(); i++) {
temp = (*this)[i] * n;
temp = static_cast<uint64_t>((*this)[i]) * n;
temp += carry;
if (temp < 10) {
carry = 0;

186
math/miller_rabin.cpp Normal file
View File

@@ -0,0 +1,186 @@
/**
* Copyright 2020 @author tjgurwara99
* @file
*
* A basic implementation of Miller-Rabin primality test.
*/
#include <cassert>
#include <iostream>
#include <random>
#include <vector>
/**
* Function to give a binary representation of a number in reverse order
* @param num integer number that we want to convert
* @return result vector of the number input in reverse binary
*/
template <typename T>
std::vector<T> reverse_binary(T num) {
std::vector<T> result;
T temp = num;
while (temp > 0) {
result.push_back(temp % 2);
temp = temp / 2;
}
return result;
}
/**
* Function for modular exponentiation.
* This function is an efficient modular exponentiation function.
* It can be used with any big integer library such as Boost multiprecision
* to give result any modular exponentiation problem relatively quickly.
* @param base number being raised to a power as integer
* @param rev_binary_exponent reverse binary of the power the base is being
* raised to
* @param mod modulo
* @return r the modular exponentiation of \f$a^{n} \equiv r \mod{m}\f$ where
* \f$n\f$ is the base 10 representation of rev_binary_exponent and \f$m = mod
* \f$ parameter.
*/
template <typename T>
T modular_exponentiation(T base, const std::vector<T> &rev_binary_exponent,
T mod) {
if (mod == 1)
return 0;
T b = 1;
if (rev_binary_exponent.size() == 0)
return b;
T A = base;
if (rev_binary_exponent[0] == 1)
b = base;
for (typename std::vector<T>::const_iterator it =
rev_binary_exponent.cbegin() + 1;
it != rev_binary_exponent.cend(); ++it) {
A = A * A % mod;
if (*it == 1)
b = A * b % mod;
}
return b;
}
/** Function for testing the conditions that are satisfied when a number is
* prime.
* @param d number such that \f$d \cdot 2^r = n - 1\f$ where \f$n = num\f$
* parameter and \f$r \geq 1\f$
* @param num number being tested for primality.
* @return 'false' if n is composite
* @return 'true' if n is (probably) prime.
*/
template <typename T>
bool miller_test(T d, T num) {
// random number seed
std::random_device rd_seed;
// random number generator
std::mt19937 gen(rd_seed());
// Uniformly distributed range [2, num - 2] for random numbers
std::uniform_int_distribution<> distribution(2, num - 2);
// Random number generated in the range [2, num -2].
T random = distribution(gen);
// vector for reverse binary of the power
std::vector<T> power = reverse_binary(d);
// x = random ^ d % num
T x = modular_exponentiation(random, power, num);
// miller conditions
if (x == 1 || x == num - 1) {
return true;
}
while (d != num - 1) {
x = (x * x) % num;
d *= 2;
if (x == 1) {
return false;
}
if (x == num - 1) {
return true;
}
}
return false;
}
/**
* Function that test (probabilistically) whether a given number is a prime
* based on the Miller-Rabin Primality Test.
* @param num number to be tested for primality.
* @param repeats number of repetitions for the test to increase probability of
* correct result.
* @return 'false' if num is composite
* @return 'true' if num is (probably) prime
*
* \detail
* First we check whether the num input is less than 4, if so we can determine
* whether this is a prime or composite by checking for 2 and 3.
* Next we check whether this num is odd (as all primes greater than 2 are odd).
* Next we write our num in the following format \f$num = 2^r \cdot d + 1\f$.
* After finding r and d for our input num, we use for loop repeat number of
* times inside which we check the miller conditions using the function
* miller_test. If miller_test returns false then the number is composite After
* the loop finishes completely without issuing a false return call, we can
* conclude that this number is probably prime.
*/
template <typename T>
bool miller_rabin_primality_test(T num, T repeats) {
if (num <= 4) {
// If num == 2 or num == 3 then prime
if (num == 2 || num == 3) {
return true;
} else {
return false;
}
}
// If num is even then not prime
if (num % 2 == 0) {
return false;
}
// Finding d and r in num = 2^r * d + 1
T d = num - 1, r = 0;
while (d % 2 == 0) {
d = d / 2;
r++;
}
for (T i = 0; i < repeats; ++i) {
if (!miller_test(d, num)) {
return false;
}
}
return true;
}
/**
* Functions for testing the miller_rabin_primality_test() function with some
* assert statements.
*/
void tests() {
// First test on 2
assert(((void)"2 is prime but function says otherwise.\n",
miller_rabin_primality_test(2, 1) == true));
std::cout << "First test passes." << std::endl;
// Second test on 5
assert(((void)"5 should be prime but the function says otherwise.\n",
miller_rabin_primality_test(5, 3) == true));
std::cout << "Second test passes." << std::endl;
// Third test on 23
assert(((void)"23 should be prime but the function says otherwise.\n",
miller_rabin_primality_test(23, 3) == true));
std::cout << "Third test passes." << std::endl;
// Fourth test on 16
assert(((void)"16 is not a prime but the function says otherwise.\n",
miller_rabin_primality_test(16, 3) == false));
std::cout << "Fourth test passes." << std::endl;
// Fifth test on 27
assert(((void)"27 is not a prime but the function says otherwise.\n",
miller_rabin_primality_test(27, 3) == false));
std::cout << "Fifth test passes." << std::endl;
}
/**
* Main function
*/
int main() {
tests();
return 0;
}

View File

@@ -14,9 +14,9 @@ void Sieve(int64_t n) {
memset(prime, '1', sizeof(prime)); // intitize '1' to every index
prime[0] = '0'; // 0 is not prime
prime[1] = '0'; // 1 is not prime
for (int p = 2; p * p <= n; p++) {
for (int64_t p = 2; p * p <= n; p++) {
if (prime[p] == '1') {
for (int i = p * p; i <= n; i += p)
for (int64_t i = p * p; i <= n; i += p)
prime[i] = '0'; // set all multiples of p to false
}
}

View File

@@ -35,7 +35,7 @@ class stats_computer1 {
n++;
T tmp = x - K;
Ex += tmp;
Ex2 += tmp * tmp;
Ex2 += static_cast<double>(tmp) * tmp;
}
/** return sample mean computed till last sample */

View File

@@ -10,25 +10,21 @@
* @see primes_up_to_billion.cpp prime_numbers.cpp
*/
#include <iostream>
/** Maximum number of primes */
#define MAX 10000000
/** array to store the primes */
bool isprime[MAX];
#include <iostream> // for io operations
/**
* This is the function that finds the primes and eliminates
* the multiples.
* @param N number of primes to check
* @param [out] isprime a boolean array of size `N` identifying if `i`^th number is prime or not
*/
void sieve(uint32_t N) {
isprime[0] = false;
isprime[1] = false;
for (uint32_t i = 2; i <= N; i++) {
if (isprime[i]) {
for (uint32_t j = (i << 1); j <= N; j += i) {
isprime[j] = false;
void sieve(uint32_t N, bool *isprime) {
isprime[0] = true;
isprime[1] = true;
for (uint32_t i = 2; i * i <= N; i++) {
if (!isprime[i]) {
for (uint32_t j = (i << 1); j <= N; j = j + i) {
isprime[j] = true;
}
}
}
@@ -36,10 +32,12 @@ void sieve(uint32_t N) {
/**
* This function prints out the primes to STDOUT
* @param N number of primes to check
* @param [in] isprime a boolean array of size `N` identifying if `i`^th number is prime or not
*/
void print(uint32_t N) {
for (uint32_t i = 1; i <= N; i++) {
if (isprime[i]) {
void print(uint32_t N, const bool *isprime) {
for (uint32_t i = 2; i <= N; i++) {
if (!isprime[i]) {
std::cout << i << ' ';
}
}
@@ -47,19 +45,14 @@ void print(uint32_t N) {
}
/**
* Initialize the array
* Main function
*/
void init() {
for (uint32_t i = 1; i < MAX; i++) {
isprime[i] = true;
}
}
/** main function */
int main() {
uint32_t N = 100;
init();
sieve(N);
print(N);
bool *isprime = new bool[N];
sieve(N, isprime);
print(N, isprime);
delete[] isprime;
return 0;
}

72
math/sum_of_digits.cpp Normal file
View File

@@ -0,0 +1,72 @@
/**
* Copyright 2020 @author iamnambiar
*
* @file
* \brief A C++ Program to find the Sum of Digits of input integer.
*/
#include <cassert>
#include <iostream>
/**
* Function to find the sum of the digits of an integer.
* @param num The integer.
* @return Sum of the digits of the integer.
*
* \detail
* First the algorithm check whether the num is negative or positive,
* if it is negative, then we neglect the negative sign.
* Next, the algorithm extract the last digit of num by dividing by 10
* and extracting the remainder and this is added to the sum.
* The number is then divided by 10 to remove the last digit.
* This loop continues until num becomes 0.
*/
int sum_of_digits(int num) {
// If num is negative then negative sign is neglected.
if (num < 0) {
num = -1 * num;
}
int sum = 0;
while (num > 0) {
sum = sum + (num % 10);
num = num / 10;
}
return sum;
}
/**
* Function for testing the sum_of_digits() function with a
* first test case of 119765 and assert statement.
*/
void test1() {
int test_case_1 = sum_of_digits(119765);
assert(test_case_1 == 29);
}
/**
* Function for testing the sum_of_digits() function with a
* second test case of -12256 and assert statement.
*/
void test2() {
int test_case_2 = sum_of_digits(-12256);
assert(test_case_2 == 16);
}
/**
* Function for testing the sum_of_digits() with
* all the test cases.
*/
void test() {
// First test.
test1();
// Second test.
test2();
}
/**
* Main Function
*/
int main() {
test();
std::cout << "Success." << std::endl;
return 0;
}

View File

@@ -0,0 +1,215 @@
/**
* \file
* \brief Find real extrema of a univariate real function in a given interval
* using [Brent's method](https://en.wikipedia.org/wiki/Brent%27s_method).
*
* Refer the algorithm discoverer's publication
* [online](https://maths-people.anu.edu.au/~brent/pd/rpb011i.pdf) and also
* associated book:
* > R. P. Brent, Algorithms for Minimization without
* > Derivatives, Prentice-Hall, Englewood Cliffs, New Jersey, 1973
*
* \see golden_search_extrema.cpp
*
* \author [Krishna Vedala](https://github.com/kvedala)
*/
#define _USE_MATH_DEFINES ///< required for MS Visual C++
#include <cassert>
#include <cmath>
#include <functional>
#include <iostream>
#include <limits>
#define EPSILON \
std::sqrt( \
std::numeric_limits<double>::epsilon()) ///< system accuracy limit
/**
* @brief Get the real root of a function in the given interval.
*
* @param f function to get root for
* @param lim_a lower limit of search window
* @param lim_b upper limit of search window
* @return root found in the interval
*/
double get_minima(const std::function<double(double)> &f, double lim_a,
double lim_b) {
uint32_t iters = 0;
if (lim_a > lim_b) {
std::swap(lim_a, lim_b);
} else if (std::abs(lim_a - lim_b) <= EPSILON) {
std::cerr << "Search range must be greater than " << EPSILON << "\n";
return lim_a;
}
// golden ratio value
const double M_GOLDEN_RATIO = (3.f - std::sqrt(5.f)) / 2.f;
double v = lim_a + M_GOLDEN_RATIO * (lim_b - lim_a);
double u, w = v, x = v;
double fu, fv = f(v);
double fw = fv, fx = fv;
double mid_point = (lim_a + lim_b) / 2.f;
double p = 0, q = 0, r = 0;
double d, e = 0;
double tolerance, tolerance2;
do {
mid_point = (lim_a + lim_b) / 2.f;
tolerance = EPSILON * std::abs(x);
tolerance2 = 2 * tolerance;
if (std::abs(e) > tolerance2) {
// fit parabola
r = (x - w) * (fx - fv);
q = (x - v) * (fx - fw);
p = (x - v) * q - (x - w) * r;
q = 2.f * (q - r);
if (q > 0)
p = -p;
else
q = -q;
r = e;
e = d;
}
if (std::abs(p) < std::abs(0.5 * q * r) && p < q * (lim_b - x)) {
// parabolic interpolation step
d = p / q;
u = x + d;
if (u - lim_a < tolerance2 || lim_b - u < tolerance2)
d = x < mid_point ? tolerance : -tolerance;
} else {
// golden section interpolation step
e = (x < mid_point ? lim_b : lim_a) - x;
d = M_GOLDEN_RATIO * e;
}
// evaluate not too close to x
if (std::abs(d) >= tolerance)
u = d;
else if (d > 0)
u = tolerance;
else
u = -tolerance;
u += x;
fu = f(u);
// update variables
if (fu <= fx) {
if (u < x)
lim_b = x;
else
lim_a = x;
v = w;
fv = fw;
w = x;
fw = fx;
x = u;
fx = fu;
} else {
if (u < x)
lim_a = u;
else
lim_b = u;
if (fu <= fw || x == w) {
v = w;
fv = fw;
w = u;
fw = fu;
} else if (fu <= fv || v == x || v == w) {
v = u;
fv = fu;
}
}
iters++;
} while (std::abs(x - mid_point) > (tolerance - (lim_b - lim_a) / 2.f));
std::cout << " (iters: " << iters << ") ";
return x;
}
/**
* @brief Test function to find root for the function
* \f$f(x)= (x-2)^2\f$
* in the interval \f$[1,5]\f$
* \n Expected result = 2
*/
void test1() {
// define the function to minimize as a lambda function
std::function<double(double)> f1 = [](double x) {
return (x - 2) * (x - 2);
};
std::cout << "Test 1.... ";
double minima = get_minima(f1, -1, 5);
std::cout << minima << "...";
assert(std::abs(minima - 2) < EPSILON);
std::cout << "passed\n";
}
/**
* @brief Test function to find root for the function
* \f$f(x)= x^{\frac{1}{x}}\f$
* in the interval \f$[-2,10]\f$
* \n Expected result: \f$e\approx 2.71828182845904509\f$
*/
void test2() {
// define the function to maximize as a lambda function
// since we are maximixing, we negated the function return value
std::function<double(double)> func = [](double x) {
return -std::pow(x, 1.f / x);
};
std::cout << "Test 2.... ";
double minima = get_minima(func, -2, 5);
std::cout << minima << " (" << M_E << ")...";
assert(std::abs(minima - M_E) < EPSILON);
std::cout << "passed\n";
}
/**
* @brief Test function to find *maxima* for the function
* \f$f(x)= \cos x\f$
* in the interval \f$[0,12]\f$
* \n Expected result: \f$\pi\approx 3.14159265358979312\f$
*/
void test3() {
// define the function to maximize as a lambda function
// since we are maximixing, we negated the function return value
std::function<double(double)> func = [](double x) { return std::cos(x); };
std::cout << "Test 3.... ";
double minima = get_minima(func, -4, 12);
std::cout << minima << " (" << M_PI << ")...";
assert(std::abs(minima - M_PI) < EPSILON);
std::cout << "passed\n";
}
/** Main function */
int main() {
std::cout.precision(18);
std::cout << "Computations performed with machine epsilon: " << EPSILON
<< "\n";
test1();
test2();
test3();
return 0;
}

View File

@@ -0,0 +1,150 @@
/**
* \file
* \brief Find extrema of a univariate real function in a given interval using
* [golden section search
* algorithm](https://en.wikipedia.org/wiki/Golden-section_search).
*
* \see brent_method_extrema.cpp
* \author [Krishna Vedala](https://github.com/kvedala)
*/
#define _USE_MATH_DEFINES //< required for MS Visual C++
#include <cassert>
#include <cmath>
#include <functional>
#include <iostream>
#include <limits>
#define EPSILON 1e-7 ///< solution accuracy limit
/**
* @brief Get the minima of a function in the given interval. To get the maxima,
* simply negate the function. The golden ratio used here is:\f[
* k=\frac{3-\sqrt{5}}{2} \approx 0.381966\ldots\f]
*
* @param f function to get minima for
* @param lim_a lower limit of search window
* @param lim_b upper limit of search window
* @return local minima found in the interval
*/
double get_minima(const std::function<double(double)> &f, double lim_a,
double lim_b) {
uint32_t iters = 0;
double c, d;
double prev_mean, mean = std::numeric_limits<double>::infinity();
// golden ratio value
const double M_GOLDEN_RATIO = (1.f + std::sqrt(5.f)) / 2.f;
// ensure that lim_a < lim_b
if (lim_a > lim_b) {
std::swap(lim_a, lim_b);
} else if (std::abs(lim_a - lim_b) <= EPSILON) {
std::cerr << "Search range must be greater than " << EPSILON << "\n";
return lim_a;
}
do {
prev_mean = mean;
// compute the section ratio width
double ratio = (lim_b - lim_a) / M_GOLDEN_RATIO;
c = lim_b - ratio; // right-side section start
d = lim_a + ratio; // left-side section end
if (f(c) < f(d)) {
// select left section
lim_b = d;
} else {
// selct right section
lim_a = c;
}
mean = (lim_a + lim_b) / 2.f;
iters++;
// continue till the interval width is greater than sqrt(system epsilon)
} while (std::abs(lim_a - lim_b) > EPSILON);
std::cout << " (iters: " << iters << ") ";
return prev_mean;
}
/**
* @brief Test function to find minima for the function
* \f$f(x)= (x-2)^2\f$
* in the interval \f$[1,5]\f$
* \n Expected result = 2
*/
void test1() {
// define the function to minimize as a lambda function
std::function<double(double)> f1 = [](double x) {
return (x - 2) * (x - 2);
};
std::cout << "Test 1.... ";
double minima = get_minima(f1, 1, 5);
std::cout << minima << "...";
assert(std::abs(minima - 2) < EPSILON);
std::cout << "passed\n";
}
/**
* @brief Test function to find *maxima* for the function
* \f$f(x)= x^{\frac{1}{x}}\f$
* in the interval \f$[-2,10]\f$
* \n Expected result: \f$e\approx 2.71828182845904509\f$
*/
void test2() {
// define the function to maximize as a lambda function
// since we are maximixing, we negated the function return value
std::function<double(double)> func = [](double x) {
return -std::pow(x, 1.f / x);
};
std::cout << "Test 2.... ";
double minima = get_minima(func, -2, 10);
std::cout << minima << " (" << M_E << ")...";
assert(std::abs(minima - M_E) < EPSILON);
std::cout << "passed\n";
}
/**
* @brief Test function to find *maxima* for the function
* \f$f(x)= \cos x\f$
* in the interval \f$[0,12]\f$
* \n Expected result: \f$\pi\approx 3.14159265358979312\f$
*/
void test3() {
// define the function to maximize as a lambda function
// since we are maximixing, we negated the function return value
std::function<double(double)> func = [](double x) { return std::cos(x); };
std::cout << "Test 3.... ";
double minima = get_minima(func, -4, 12);
std::cout << minima << " (" << M_PI << ")...";
assert(std::abs(minima - M_PI) < EPSILON);
std::cout << "passed\n";
}
/** Main function */
int main() {
std::cout.precision(9);
std::cout << "Computations performed with machine epsilon: " << EPSILON
<< "\n";
test1();
test2();
test3();
return 0;
}

View File

@@ -4,76 +4,18 @@
* square matrix
* \author [Krishna Vedala](https://github.com/kvedala)
*/
#include <cassert>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <vector>
#ifdef _OPENMP
#include <omp.h>
#endif
/** Perform LU decomposition on matrix
* \param[in] A matrix to decompose
* \param[out] L output L matrix
* \param[out] U output U matrix
* \returns 0 if no errors
* \returns negative if error occurred
*/
int lu_decomposition(const std::vector<std::vector<double>> &A,
std::vector<std::vector<double>> *L,
std::vector<std::vector<double>> *U) {
int row, col, j;
int mat_size = A.size();
if (mat_size != A[0].size()) {
// check matrix is a square matrix
std::cerr << "Not a square matrix!\n";
return -1;
}
// regularize each row
for (row = 0; row < mat_size; row++) {
// Upper triangular matrix
#ifdef _OPENMP
#pragma omp for
#endif
for (col = row; col < mat_size; col++) {
// Summation of L[i,j] * U[j,k]
double lu_sum = 0.;
for (j = 0; j < row; j++) lu_sum += L[0][row][j] * U[0][j][col];
// Evaluate U[i,k]
U[0][row][col] = A[row][col] - lu_sum;
}
// Lower triangular matrix
#ifdef _OPENMP
#pragma omp for
#endif
for (col = row; col < mat_size; col++) {
if (row == col) {
L[0][row][col] = 1.;
continue;
}
// Summation of L[i,j] * U[j,k]
double lu_sum = 0.;
for (j = 0; j < row; j++) lu_sum += L[0][col][j] * U[0][j][row];
// Evaluate U[i,k]
L[0][col][row] = (A[col][row] - lu_sum) / U[0][row][row];
}
}
return 0;
}
#include "./lu_decomposition.h"
/**
* operator to print a matrix
*/
template <typename T>
std::ostream &operator<<(std::ostream &out,
std::vector<std::vector<T>> const &v) {
std::ostream &operator<<(std::ostream &out, matrix<T> const &v) {
const int width = 10;
const char separator = ' ';
@@ -87,26 +29,21 @@ std::ostream &operator<<(std::ostream &out,
return out;
}
/** Main function */
int main(int argc, char **argv) {
/**
* Test LU decomposition
* \todo better ways to self-check a matrix output?
*/
void test1() {
int mat_size = 3; // default matrix size
const int range = 50;
const int range2 = range >> 1;
if (argc == 2)
mat_size = atoi(argv[1]);
std::srand(std::time(NULL)); // random number initializer
/* Create a square matrix with random values */
std::vector<std::vector<double>> A(mat_size);
std::vector<std::vector<double>> L(mat_size); // output
std::vector<std::vector<double>> U(mat_size); // output
matrix<double> A(mat_size, std::valarray<double>(mat_size));
matrix<double> L(mat_size, std::valarray<double>(mat_size)); // output
matrix<double> U(mat_size, std::valarray<double>(mat_size)); // output
for (int i = 0; i < mat_size; i++) {
// calloc so that all valeus are '0' by default
A[i] = std::vector<double>(mat_size);
L[i] = std::vector<double>(mat_size);
U[i] = std::vector<double>(mat_size);
for (int j = 0; j < mat_size; j++)
/* create random values in the limits [-range2, range-1] */
A[i][j] = static_cast<double>(std::rand() % range - range2);
@@ -121,6 +58,33 @@ int main(int argc, char **argv) {
std::cout << "A = \n" << A << "\n";
std::cout << "L = \n" << L << "\n";
std::cout << "U = \n" << U << "\n";
}
/**
* Test determinant computation using LU decomposition
*/
void test2() {
std::cout << "Determinant test 1...";
matrix<int> A1({{1, 2, 3}, {4, 9, 6}, {7, 8, 9}});
assert(determinant_lu(A1) == -48);
std::cout << "passed\n";
std::cout << "Determinant test 2...";
matrix<int> A2({{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
assert(determinant_lu(A2) == 0);
std::cout << "passed\n";
std::cout << "Determinant test 3...";
matrix<float> A3({{1.2, 2.3, 3.4}, {4.5, 5.6, 6.7}, {7.8, 8.9, 9.0}});
assert(determinant_lu(A3) == 3.63);
std::cout << "passed\n";
}
/** Main function */
int main(int argc, char **argv) {
std::srand(std::time(NULL)); // random number initializer
test1();
test2();
return 0;
}

View File

@@ -0,0 +1,102 @@
/**
* @file lu_decomposition.h
* @author [Krishna Vedala](https://github.com/kvedala)
* @brief Functions associated with [LU
* Decomposition](https://en.wikipedia.org/wiki/LU_decomposition)
* of a square matrix.
*/
#pragma once
#include <iostream>
#include <valarray>
#include <vector>
#ifdef _OPENMP
#include <omp.h>
#endif
/** Define matrix type as a `std::vector` of `std::valarray` */
template <typename T>
using matrix = std::vector<std::valarray<T>>;
/** Perform LU decomposition on matrix
* \param[in] A matrix to decompose
* \param[out] L output L matrix
* \param[out] U output U matrix
* \returns 0 if no errors
* \returns negative if error occurred
*/
template <typename T>
int lu_decomposition(const matrix<T> &A, matrix<double> *L, matrix<double> *U) {
int row, col, j;
int mat_size = A.size();
if (mat_size != A[0].size()) {
// check matrix is a square matrix
std::cerr << "Not a square matrix!\n";
return -1;
}
// regularize each row
for (row = 0; row < mat_size; row++) {
// Upper triangular matrix
#ifdef _OPENMP
#pragma omp for
#endif
for (col = row; col < mat_size; col++) {
// Summation of L[i,j] * U[j,k]
double lu_sum = 0.;
for (j = 0; j < row; j++) {
lu_sum += L[0][row][j] * U[0][j][col];
}
// Evaluate U[i,k]
U[0][row][col] = A[row][col] - lu_sum;
}
// Lower triangular matrix
#ifdef _OPENMP
#pragma omp for
#endif
for (col = row; col < mat_size; col++) {
if (row == col) {
L[0][row][col] = 1.;
continue;
}
// Summation of L[i,j] * U[j,k]
double lu_sum = 0.;
for (j = 0; j < row; j++) {
lu_sum += L[0][col][j] * U[0][j][row];
}
// Evaluate U[i,k]
L[0][col][row] = (A[col][row] - lu_sum) / U[0][row][row];
}
}
return 0;
}
/**
* Compute determinant of an NxN square matrix using LU decomposition.
* Using LU decomposition, the determinant is given by the product of diagonal
* elements of matrices L and U.
*
* @tparam T datatype of input matrix - int, unsigned int, double, etc
* @param A input square matrix
* @return determinant of matrix A
*/
template <typename T>
double determinant_lu(const matrix<T> &A) {
matrix<double> L(A.size(), std::valarray<double>(A.size()));
matrix<double> U(A.size(), std::valarray<double>(A.size()));
if (lu_decomposition(A, &L, &U) < 0)
return 0;
double result = 1.f;
for (size_t i = 0; i < A.size(); i++) {
result *= L[i][i] * U[i][i];
}
return result;
}

View File

@@ -17,17 +17,24 @@
#include <iostream>
#include <limits>
#define EPSILON \
1e-6 // std::numeric_limits<double>::epsilon() ///< system accuracy limit
#define MAX_ITERATIONS 50000 ///< Maximum number of iterations to check
constexpr double EPSILON = 1e-10; ///< system accuracy limit
constexpr int16_t MAX_ITERATIONS = INT16_MAX; ///< Maximum number of iterations
/** define \f$f(x)\f$ to find root for
/** define \f$f(x)\f$ to find root for.
* Currently defined as:
* \f[
* f(x) = x^3 - 4x - 9
* \f]
*/
static double eq(double i) {
return (std::pow(i, 3) - (4 * i) - 9); // original equation
}
/** define the derivative function \f$f'(x)\f$
* For the current problem, it is:
* \f[
* f'(x) = 3x^2 - 4
* \f]
*/
static double eq_der(double i) {
return ((3 * std::pow(i, 2)) - 4); // derivative of equation
@@ -37,8 +44,8 @@ static double eq_der(double i) {
int main() {
std::srand(std::time(nullptr)); // initialize randomizer
double z, c = std::rand() % 100, m, n;
int i;
double z = NAN, c = std::rand() % 100, m = NAN, n = NAN;
int i = 0;
std::cout << "\nInitial approximation: " << c;
@@ -50,8 +57,9 @@ int main() {
z = c - (m / n);
c = z;
if (std::abs(m) < EPSILON) // stoping criteria
if (std::abs(m) < EPSILON) { // stoping criteria
break;
}
}
std::cout << "\n\nRoot: " << z << "\t\tSteps: " << i << std::endl;

View File

@@ -54,8 +54,8 @@
void problem(const double &x, std::valarray<double> *y,
std::valarray<double> *dy) {
const double omega = 1.F; // some const for the problem
dy[0][0] = y[0][1]; // x dot
dy[0][1] = -omega * omega * y[0][0]; // y dot
(*dy)[0] = (*y)[1]; // x dot // NOLINT
(*dy)[1] = -omega * omega * (*y)[0]; // y dot // NOLINT
}
/**
@@ -83,10 +83,10 @@ void exact_solution(const double &x, std::valarray<double> *y) {
* @param[in,out] y take \f$y_n\f$ and compute \f$y_{n+1}\f$
* @param[in,out] dy compute \f$f\left(x_n,y_n\right)\f$
*/
void forward_euler_step(const double dx, const double &x,
void forward_euler_step(const double dx, const double x,
std::valarray<double> *y, std::valarray<double> *dy) {
problem(x, y, dy);
y[0] += dy[0] * dx;
*y += *dy * dx;
}
/**
@@ -101,7 +101,7 @@ void forward_euler_step(const double dx, const double &x,
*/
double forward_euler(double dx, double x0, double x_max,
std::valarray<double> *y, bool save_to_file = false) {
std::valarray<double> dy = y[0];
std::valarray<double> dy = *y;
std::ofstream fp;
if (save_to_file) {
@@ -122,9 +122,9 @@ double forward_euler(double dx, double x0, double x_max,
// write to file
fp << x << ",";
for (int i = 0; i < L - 1; i++) {
fp << y[0][i] << ",";
fp << y[0][i] << ","; // NOLINT
}
fp << y[0][L - 1] << "\n";
fp << y[0][L - 1] << "\n"; // NOLINT
}
forward_euler_step(dx, x, y, &dy); // perform integration
@@ -133,8 +133,9 @@ double forward_euler(double dx, double x0, double x_max,
/* end of integration */
std::clock_t t2 = std::clock();
if (fp.is_open())
if (fp.is_open()) {
fp.close();
}
return static_cast<double>(t2 - t1) / CLOCKS_PER_SEC;
}
@@ -153,7 +154,7 @@ void save_exact_solution(const double &X0, const double &X_MAX,
const double &step_size,
const std::valarray<double> &Y0) {
double x = X0;
std::valarray<double> y = Y0;
std::valarray<double> y(Y0);
std::ofstream fp("exact.csv", std::ostream::out);
if (!fp.is_open()) {
@@ -166,9 +167,9 @@ void save_exact_solution(const double &X0, const double &X_MAX,
do {
fp << x << ",";
for (int i = 0; i < y.size() - 1; i++) {
fp << y[i] << ",";
fp << y[i] << ","; // NOLINT
}
fp << y[y.size() - 1] << "\n";
fp << y[y.size() - 1] << "\n"; // NOLINT
exact_solution(x, &y);
@@ -186,10 +187,10 @@ void save_exact_solution(const double &X0, const double &X_MAX,
* Main Function
*/
int main(int argc, char *argv[]) {
double X0 = 0.f; /* initial value of x0 */
double X_MAX = 10.F; /* upper limit of integration */
std::valarray<double> Y0 = {1.f, 0.f}; /* initial value Y = y(x = x_0) */
double step_size;
double X0 = 0.f; /* initial value of x0 */
double X_MAX = 10.F; /* upper limit of integration */
std::valarray<double> Y0{1.f, 0.f}; /* initial value Y = y(x = x_0) */
double step_size = NAN;
if (argc == 1) {
std::cout << "\nEnter the step size: ";

View File

@@ -39,18 +39,15 @@ using std::vector;
#define pb push_back
#define MOD 1000000007
/** returns absolute value */
inline ll ab(ll x) { return x > 0LL ? x : -x; }
/** global variable k
/** global variable mat_size
* @todo @stepfencurryxiao add documetnation
*/
ll k;
ll mat_size;
/** global vector variables
/** global vector variables used in the ::ans function.
* @todo @stepfencurryxiao add documetnation
*/
vector<ll> a, b, c;
vector<ll> fib_b, fib_c;
/** To multiply 2 matrices
* \param [in] A matrix 1 of size (m\f$\times\f$n)
@@ -59,10 +56,10 @@ vector<ll> a, b, c;
*/
vector<vector<ll>> multiply(const vector<vector<ll>> &A,
const vector<vector<ll>> &B) {
vector<vector<ll>> C(k + 1, vector<ll>(k + 1));
for (ll i = 1; i <= k; i++) {
for (ll j = 1; j <= k; j++) {
for (ll z = 1; z <= k; z++) {
vector<vector<ll>> C(mat_size + 1, vector<ll>(mat_size + 1));
for (ll i = 1; i <= mat_size; i++) {
for (ll j = 1; j <= mat_size; j++) {
for (ll z = 1; z <= mat_size; z++) {
C[i][j] = (C[i][j] + (A[i][z] * B[z][j]) % MOD) % MOD;
}
}
@@ -94,24 +91,24 @@ vector<vector<ll>> power(const vector<vector<ll>> &A, ll p) {
ll ans(ll n) {
if (n == 0)
return 0;
if (n <= k)
return b[n - 1];
if (n <= mat_size)
return fib_b[n - 1];
// F1
vector<ll> F1(k + 1);
for (ll i = 1; i <= k; i++) F1[i] = b[i - 1];
vector<ll> F1(mat_size + 1);
for (ll i = 1; i <= mat_size; i++) F1[i] = fib_b[i - 1];
// Transpose matrix
vector<vector<ll>> T(k + 1, vector<ll>(k + 1));
for (ll i = 1; i <= k; i++) {
for (ll j = 1; j <= k; j++) {
if (i < k) {
vector<vector<ll>> T(mat_size + 1, vector<ll>(mat_size + 1));
for (ll i = 1; i <= mat_size; i++) {
for (ll j = 1; j <= mat_size; j++) {
if (i < mat_size) {
if (j == i + 1)
T[i][j] = 1;
else
T[i][j] = 0;
continue;
}
T[i][j] = c[k - j];
T[i][j] = fib_c[mat_size - j];
}
}
// T^n-1
@@ -119,7 +116,7 @@ ll ans(ll n) {
// T*F1
ll res = 0;
for (ll i = 1; i <= k; i++) {
for (ll i = 1; i <= mat_size; i++) {
res = (res + (T[1][i] * F1[i]) % MOD) % MOD;
}
return res;
@@ -133,19 +130,19 @@ int main() {
cin >> t;
ll i, j, x;
while (t--) {
cin >> k;
for (i = 0; i < k; i++) {
cin >> mat_size;
for (i = 0; i < mat_size; i++) {
cin >> x;
b.pb(x);
fib_b.pb(x);
}
for (i = 0; i < k; i++) {
for (i = 0; i < mat_size; i++) {
cin >> x;
c.pb(x);
fib_c.pb(x);
}
cin >> x;
cout << ans(x) << endl;
b.clear();
c.clear();
fib_b.clear();
fib_c.clear();
}
return 0;
}

View File

@@ -20,13 +20,13 @@
char stack[MAX];
//! pointer to track stack index
int top = -1;
int stack_idx = -1;
//! push byte to stack variable
void push(char ch) { stack[++top] = ch; }
void push(char ch) { stack[++stack_idx] = ch; }
//! pop a byte out of stack variable
char pop() { return stack[top--]; }
char pop() { return stack[stack_idx--]; }
//! @}-------------- end stack -----------
@@ -56,7 +56,7 @@ int main() {
while (valid == 1 && i < exp.length()) {
if (exp[i] == '(' || exp[i] == '{' || exp[i] == '[' || exp[i] == '<') {
push(exp[i]);
} else if (top >= 0 && stack[top] == opening(exp[i])) {
} else if (stack_idx >= 0 && stack[stack_idx] == opening(exp[i])) {
pop();
} else {
valid = 0;
@@ -65,7 +65,7 @@ int main() {
}
// makes sure the stack is empty after processsing (above)
if (valid == 1 && top == -1) {
if (valid == 1 && stack_idx == -1) {
std::cout << "\nCorrect Expression";
} else {
std::cout << "\nWrong Expression";

View File

@@ -1,60 +0,0 @@
// Binary Indexed Tree.
#include <iostream>
using namespace std;
class Bit {
int n;
vector<int> bit;
inline int offset(int x) { return (x & (-x)); }
public:
Bit(vector<int>& arr) {
n = arr.size();
bit.assign(n + 1, 0);
for (int i = 0; i < n; ++i) {
update(i, arr[i]);
}
}
Bit(int x) {
n = x;
bit.assign(n + 1, 0);
}
void update(int id, int val) {
// Add val at id
id++;
while (id <= n) {
bit[id] += val;
id += offset(id);
}
}
int sum(int id) {
// Get prefix sum upto id.
id++;
int res = 0;
while (id > 0) {
res += bit[id];
id -= offset(id);
}
return res;
}
int sum_range(int l, int r) { return sum(r) - sum(l - 1); }
};
int main() {
int n = 5;
vector<int> arr = {1, 2, 3, 4, 5};
Bit x(arr);
assert(x.sum_range(0, 0) == 1);
assert(x.sum_range(0, 1) == 3);
assert(x.sum_range(0, 2) == 6);
x.update(0, 6);
assert(x.sum_range(0, 0) == 6);
assert(x.sum_range(0, 1) == 8);
assert(x.sum_range(0, 2) == 11);
return 0;
}

View File

@@ -0,0 +1,82 @@
/**
* @file
* @brief Fenwick tree
*
* 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.
*/
#include <cassert>
#include <iostream>
#include <vector>
/**
* n --> No. of elements present in input array.
* bit[0..n] --> Array that represents Binary Indexed Tree.
*/
class FenwickTree {
int n;
std::vector<int> bit;
/** Returns the highest power of two which is not more than x */
inline int offset(int x) { return (x & (-x)); }
public:
/** Constructor
* \param[in] arr --> Input array for which prefix sum is evaluated.
*/
explicit FenwickTree(const std::vector<int>& 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.
*/
explicit FenwickTree(int x) {
n = x;
bit.assign(n + 1, 0);
}
/** Add val at id */
void update(int id, int val) {
id++;
while (id <= n) {
bit[id] += val;
id += offset(id);
}
}
/** Get prefix sum upto id */
int sum(int id) {
id++;
int res = 0;
while (id > 0) {
res += bit[id];
id -= offset(id);
}
return res;
}
/** Returns the prefix sum in range from l to r */
int sum_range(int l, int r) { return sum(r) - sum(l - 1); }
};
/** Main function */
int main() {
int n = 5;
std::vector<int> arr = {1, 2, 3, 4, 5};
FenwickTree 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);
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);
return 0;
}

View File

@@ -1,54 +0,0 @@
#include <iostream>
using namespace std;
/**
* ` lowbit(x) ` aims to find the last 1 in binary of a positive number
* twos complement works good on this
* also using ` x - (x & (x - 1)) `
*/
#define lowbit(x) (x & (-x))
const int maxn = 1e5 + 7;
int tree[maxn] = {0},
range; // segement of [1...range], notice it must be less than `maxn`
void update(int x, int c) {
while (x <= range) {
tree[x] += c;
x += lowbit(x);
}
}
int query(int x) {
int ans = 0;
while (x) {
ans += tree[x];
x -= lowbit(x);
}
return ans;
}
int query_segement(int l, int r) { return query(r) - query(l - 1); }
int main() {
cin >> range;
for (int i = 1; i <= range; i++) {
int num;
cin >> num;
update(i, num);
}
int q;
cin >> q;
while (q--) {
int op;
cin >> op;
if (op == 0) {
int l, r;
cin >> l >> r;
cout << query_segement(l, r) << endl;
} else {
int x, c;
cin >> x >> c;
update(x, c);
}
}
return 0;
}

127
search/fibonacci_search.cpp Normal file
View File

@@ -0,0 +1,127 @@
/**
* @author sprintyaf
* @file fibonacci_search.cpp
* @brief [Fibonacci search
* algorithm](https://en.wikipedia.org/wiki/Fibonacci_search_technique)
*/
#include <iostream>
#include <vector> // for std::vector class
#include <cassert> // for assert
#include <cstdlib> // for random numbers
#include <algorithm> // for sorting
/**
* @brief using fibonacci search algorithm finds an index of a given element in a sorted array
*
* @param arr sorted array
* @param value value that we're looking for
* @returns if the array contains the value, returns an index of the element. otherwise -1.
*/
int fibonacci_search(const std::vector<int> &arr, int value){
// initialize last and current members of Fibonacci sequence
int last = 0, current = 1;
int length = arr.size(); // array size
// next member of Fibonacci sequence which is "last" + "current"
int next = last + current;
// "next" will store the smallest Fibonacci number greater or equal to "length"
while(next < length){
last = current;
current = next;
next = last + current;
}
// "offset" is the end of eliminated range from front
int offset = -1, index;
// while loop until there are elements left to consider.
// when "next" becomes 1, last is equal to 0, so search is done,
// because arr[offset] will already be eliminated
while(next > 1){
// check if "last" is valid location
index = std::min(offset + last, length-1);
// if value is greater than the value at "index", eliminate the subarray from offset to index
if(arr[index] < value){
next = current;
current = last;
last = next - current;
offset = index;
// if value is less than the value at "index", eliminate the subarray after index+1
} else if(arr[index] > value){
next = last;
current = current - last;
last = next - current;
// element is found
} else {
return index;
}
}
// comparing the last element
if(current && !arr.empty() && arr[offset+1] == value){
return offset+1;
}
// value was not found, return -1
return -1;
}
/**
* @brief random tests for checking performance when an array doesn't contain an element
*/
bool no_occurence_tests(){
bool passed = true;
int rand_num, rand_value, index, num_tests = 1000;
std::vector<int> arr;
while(num_tests--){
arr.clear();
for(int i = 0; i < 100; i++){
rand_num = std::rand() % 1000;
arr.push_back(rand_num);
}
rand_value = std::rand() % 1000;
while(std::find(arr.begin(), arr.end(), rand_value) != arr.end()){
std::remove(arr.begin(), arr.end(), rand_value);
}
sort(arr.begin(), arr.end());
index = fibonacci_search(arr, rand_value);
passed = passed && (index == -1);
}
return passed;
}
/**
* @brief random tests which cover cases when we have one, multiple or zero occurences of the value we're looking for
*/
bool random_tests(){
bool passed = true;
int rand_num, rand_value, index, real_value, num_tests = 10000;
std::vector<int> arr;
while(num_tests--){
arr.clear();
for(int i = 0; i < 100; i++){
rand_num = std::rand() % 1000;
arr.push_back(rand_num);
}
rand_value = std::rand() % 1000;
std::sort(arr.begin(), arr.end());
index = fibonacci_search(arr, rand_value);
if(index != -1){
real_value = arr[index];
passed = passed && (real_value == rand_value);
} else {
passed = passed && (std::find(arr.begin(), arr.end(), rand_value) == arr.end());
}
}
return passed;
}
/**
* Main Function
* testing the algorithm
*/
int main() {
assert(no_occurence_tests());
assert(random_tests());
return 0;
}

View File

@@ -14,7 +14,7 @@ void beadSort(int *a, int len) {
// allocating memory
unsigned char *beads = new unsigned char[max * len];
memset(beads, 0, max * len);
memset(beads, 0, static_cast<size_t>(max) * len);
// mark the beads
for (int i = 0; i < len; i++)

115
sorting/bogo_sort.cpp Normal file
View File

@@ -0,0 +1,115 @@
/**
* @file
* @brief Implementation of [Bogosort algorithm](https://en.wikipedia.org/wiki/Bogosort)
*
* @details
* In computer science, bogosort (also known as permutation sort, stupid sort, slowsort,
* shotgun sort, random sort, monkey sort, bobosort or shuffle sort) is a highly inefficient
* sorting algorithm based on the generate and test paradigm. Two versions of this algorithm
* exist: a deterministic version that enumerates all permutations until it hits a sorted one,
* and a randomized version that randomly permutes its input.Randomized version is implemented here.
*
* Algorithm -
*
* Shuffle the array untill array is sorted.
*
*/
#include <iostream>
#include <algorithm>
#include <array>
#include <cassert>
/**
* @namespace sorting
* @brief Sorting algorithms
*/
namespace sorting {
/**
* Function to shuffle the elements of an array. (for reference)
* @tparam T typename of the array
* @tparam N length of array
* @param arr array to shuffle
* @returns new array with elements shuffled from a given array
*/
template <typename T, size_t N>
std::array <T, N> shuffle (std::array <T, N> arr) {
for (int i = 0; i < N; i++) {
// Swaps i'th index with random index (less than array size)
std::swap(arr[i], arr[std::rand() % N]);
}
return arr;
}
/**
* Implement randomized Bogosort algorithm and sort the elements of a given array.
* @tparam T typename of the array
* @tparam N length of array
* @param arr array to sort
* @returns new array with elements sorted from a given array
*/
template <typename T, size_t N>
std::array <T, N> randomized_bogosort (std::array <T, N> arr) {
// Untill array is not sorted
while (!std::is_sorted(arr.begin(), arr.end())) {
std::random_shuffle(arr.begin(), arr.end());// Shuffle the array
}
return arr;
}
} // namespace sorting
/**
* Function to display array on screen
* @tparam T typename of the array
* @tparam N length of array
* @param arr array to display
*/
template <typename T, size_t N>
void show_array (const std::array <T, N> &arr) {
for (int x : arr) {
std::cout << x << ' ';
}
std::cout << '\n';
}
/**
* Function to test above algorithm
*/
void test() {
// Test 1
std::array <int, 5> arr1;
for (int &x : arr1) {
x = std::rand() % 100;
}
std::cout << "Original Array : ";
show_array(arr1);
arr1 = sorting::randomized_bogosort(arr1);
std::cout << "Sorted Array : ";
show_array(arr1);
assert(std::is_sorted(arr1.begin(), arr1.end()));
// Test 2
std::array <int, 5> arr2;
for (int &x : arr2) {
x = std::rand() % 100;
}
std::cout << "Original Array : ";
show_array(arr2);
arr2 = sorting::randomized_bogosort(arr2);
std::cout << "Sorted Array : ";
show_array(arr2);
assert(std::is_sorted(arr2.begin(), arr2.end()));
}
/** Driver Code */
int main() {
// Testing
test();
// Example Usage
std::array <int, 5> arr = {3, 7, 10, 4, 1}; // Defining array which we want to sort
std::cout << "Original Array : ";
show_array(arr);
arr = sorting::randomized_bogosort(arr); // Callling bogo sort on it
std::cout << "Sorted Array : ";
show_array(arr); // Printing sorted array
return 0;
}

View File

@@ -1,49 +1,101 @@
// Kind of better version of Bubble sort.
// While Bubble sort is comparering adjacent value, Combsort is using gap larger
// than 1 Best case: O(n) Worst case: O(n ^ 2)
/**
*
* \file
* \brief [Comb Sort Algorithm
* (Comb Sort)](https://en.wikipedia.org/wiki/Comb_sort)
*
* \author
*
* \details
* - A better version of bubble sort algorithm
* - Bubble sort compares adjacent values whereas comb sort uses gap larger
* than 1
* - Best case Time complexity O(n)
* Worst case Time complexity O(n^2)
*
*/
#include <algorithm>
#include <cassert>
#include <iostream>
int a[100005];
int n;
/**
*
* Find the next gap by shrinking the current gap by shrink factor of 1.3
* @param gap current gap
* @return new gap
*
*/
int FindNextGap(int gap) {
gap = (gap * 10) / 13;
int FindNextGap(int x) {
x = (x * 10) / 13;
return std::max(1, x);
return std::max(1, gap);
}
void CombSort(int a[], int l, int r) {
// Init gap
int gap = n;
/** Function to sort array
*
* @param arr array to be sorted
* @param l start index of array
* @param r end index of array
*
*/
void CombSort(int *arr, int l, int r) {
/**
*
* initial gap will be maximum and the maximum possible value is
* the size of the array that is n and which is equal to r in this
* case so to avoid passing an extra parameter n that is the size of
* the array we are using r to initialize the initial gap.
*
*/
int gap = r;
// Initialize swapped as true to make sure that loop runs
/// Initialize swapped as true to make sure that loop runs
bool swapped = true;
// Keep running until gap = 1 or none elements were swapped
/// Keep running until gap = 1 or none elements were swapped
while (gap != 1 || swapped) {
// Find next gap
/// Find next gap
gap = FindNextGap(gap);
swapped = false;
// Compare all elements with current gap
/// Compare all elements with current gap
for (int i = l; i <= r - gap; ++i) {
if (a[i] > a[i + gap]) {
std::swap(a[i], a[i + gap]);
if (arr[i] > arr[i + gap]) {
std::swap(arr[i], arr[i + gap]);
swapped = true;
}
}
}
}
void tests() {
/// Test 1
int arr1[10] = {34, 56, 6, 23, 76, 34, 76, 343, 4, 76};
CombSort(arr1, 0, 10);
assert(std::is_sorted(arr1, arr1 + 10));
std::cout << "Test 1 passed\n";
/// Test 2
int arr2[8] = {-6, 56, -45, 56, 0, -1, 8, 8};
CombSort(arr2, 0, 8);
assert(std::is_sorted(arr2, arr2 + 8));
std::cout << "Test 2 Passed\n";
}
/** Main function */
int main() {
/// Running predefined tests
tests();
/// For user interaction
int n;
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
CombSort(a, 1, n);
for (int i = 1; i <= n; ++i) std::cout << a[i] << ' ';
int *arr = new int[n];
for (int i = 0; i < n; ++i) std::cin >> arr[i];
CombSort(arr, 0, n);
for (int i = 0; i < n; ++i) std::cout << arr[i] << ' ';
delete[] arr;
return 0;
}

133
sorting/gnome_sort.cpp Normal file
View File

@@ -0,0 +1,133 @@
/**
* @file
* @brief Implementation of [gnome
* sort](https://en.wikipedia.org/wiki/Gnome_sort) algorithm.
* @author [beqakd](https://github.com/beqakd)
* @author [Krishna Vedala](https://github.com/kvedala)
* @details
* Gnome sort algorithm is not the best one but it is widely used.
* The algorithm iteratively checks the order of pairs in the array. If they are
* on right order it moves to the next successive pair, otherwise it swaps
* elements. This operation is repeated until no more swaps are made thus
* indicating the values to be in ascending order.
*
* The time Complexity of the algorithm is \f$O(n^2)\f$ and in some cases it
* can be \f$O(n)\f$.
*/
#include <algorithm> // for std::swap
#include <array> // for std::array
#include <cassert> // for assertions
#include <iostream> // for io operations
/**
* @namespace sorting
* Sorting algorithms
*/
namespace sorting {
/**
* This implementation is for a C-style array input that gets modified in place.
* @param [in,out] arr our array of elements.
* @param size size of given array
*/
template <typename T>
void gnomeSort(T *arr, int size) {
// few easy cases
if (size <= 1) {
return;
}
int index = 0; // initialize some variables.
while (index < size) {
// check for swap
if ((index == 0) || (arr[index] >= arr[index - 1])) {
index++;
} else {
std::swap(arr[index], arr[index - 1]); // swap
index--;
}
}
}
/**
* This implementation is for a C++-style array input. The function argument is
* a pass-by-value and hence a copy of the array gets created which is then
* modified by the function and returned.
* @tparam T type of data variables in the array
* @tparam size size of the array
* @param [in] arr our array of elements.
* @return array with elements sorted
*/
template <typename T, size_t size>
std::array<T, size> gnomeSort(std::array<T, size> arr) {
// few easy cases
if (size <= 1) {
return arr;
}
int index = 0; // initialize loop index
while (index < size) {
// check for swap
if ((index == 0) || (arr[index] >= arr[index - 1])) {
index++;
} else {
std::swap(arr[index], arr[index - 1]); // swap
index--;
}
}
return arr;
}
} // namespace sorting
/**
* Test function
*/
static void test() {
// Example 1. Creating array of int,
std::cout << "Test 1 - as a C-array...";
const int size = 6;
std::array<int, size> arr = {-22, 100, 150, 35, -10, 99};
sorting::gnomeSort(arr.data(),
size); // pass array data as a C-style array pointer
assert(std::is_sorted(std::begin(arr), std::end(arr)));
std::cout << " Passed\n";
for (int i = 0; i < size; i++) {
std::cout << arr[i] << ", ";
}
std::cout << std::endl;
// Example 2. Creating array of doubles.
std::cout << "\nTest 2 - as a std::array...";
std::array<double, size> double_arr = {-100.2, 10.2, 20.0, 9.0, 7.5, 7.2};
std::array<double, size> sorted_arr = sorting::gnomeSort(double_arr);
assert(std::is_sorted(std::begin(sorted_arr), std::end(sorted_arr)));
std::cout << " Passed\n";
for (int i = 0; i < size; i++) {
std::cout << double_arr[i] << ", ";
}
std::cout << std::endl;
// Example 3. Creating random array of float.
std::cout << "\nTest 3 - 200 random numbers as a std::array...";
const int size2 = 200;
std::array<float, size2> rand_arr{};
for (auto &a : rand_arr) {
// generate random numbers between -5.0 and 4.99
a = float(std::rand() % 1000 - 500) / 100.f;
}
std::array<float, size2> float_arr = sorting::gnomeSort(rand_arr);
assert(std::is_sorted(std::begin(float_arr), std::end(float_arr)));
std::cout << " Passed\n";
// for (int i = 0; i < size; i++) std::cout << double_arr[i] << ", ";
std::cout << std::endl;
}
/**
* Our main function with example of sort method.
*/
int main() {
test();
return 0;
}

View File

@@ -1,52 +1,123 @@
/**
* \file
* \brief [Heap Sort Algorithm
* (heap sort)](https://en.wikipedia.org/wiki/Heapsort) implementation
*
* \author [Ayaan Khan](http://github.com/ayaankhan98)
*
* \details
* Heap-sort is a comparison-based sorting algorithm.
* Heap-sort can be thought of as an improved selection sort:
* like selection sort, heap sort divides its input into a sorted
* and an unsorted region, and it iteratively shrinks the unsorted
* region by extracting the largest element from it and inserting
* it into the sorted region. Unlike selection sort,
* heap sort does not waste time with a linear-time scan of the
* unsorted region; rather, heap sort maintains the unsorted region
* in a heap data structure to more quickly find the largest element
* in each step.
*
* Time Complexity - \f$O(n \log(n))\f$
*
*/
#include <algorithm>
#include <cassert>
#include <iostream>
void heapify(int *a, int i, int n) {
int largest = i;
const int l = 2 * i + 1;
const int r = 2 * i + 2;
/**
*
* Utility function to print the array after
* sorting.
*
* @param arr array to be printed
* @param sz size of array
*
*/
template <typename T>
void printArray(T *arr, int sz) {
for (int i = 0; i < sz; i++) std::cout << arr[i] << " ";
std::cout << "\n";
}
if (l < n && a[l] > a[largest])
/**
*
* \addtogroup sorting Sorting Algorithm
* @{
*
* The heapify procedure can be thought of as building a heap from
* the bottom up by successively sifting downward to establish the
* heap property.
*
* @param arr array to be sorted
* @param n size of array
* @param i node position in Binary Tress or element position in
* Array to be compared with it's childern
*
*/
template <typename T>
void heapify(T *arr, int n, int i) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest])
largest = l;
if (r < n && a[r] > a[largest])
if (r < n && arr[r] > arr[largest])
largest = r;
if (largest != i) {
std::swap(a[i], a[largest]);
heapify(a, n, largest);
std::swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
void heapsort(int *a, int n) {
for (int i = n - 1; i >= 0; --i) {
std::swap(a[0], a[i]);
heapify(a, 0, i);
/**
* Utilizes heapify procedure to sort
* the array
*
* @param arr array to be sorted
* @param n size of array
*
*/
template <typename T>
void heapSort(T *arr, int n) {
for (int i = n - 1; i >= 0; i--) heapify(arr, n, i);
for (int i = n - 1; i >= 0; i--) {
std::swap(arr[0], arr[i]);
heapify(arr, i, 0);
}
}
void build_maxheap(int *a, int n) {
for (int i = n / 2 - 1; i >= 0; --i) {
heapify(a, i, n);
}
/**
*
* @}
* Test cases to test the program
*
*/
void test() {
std::cout << "Test 1\n";
int arr[] = {-10, 78, -1, -6, 7, 4, 94, 5, 99, 0};
int sz = sizeof(arr) / sizeof(arr[0]); // sz - size of array
printArray(arr, sz); // displaying the array before sorting
heapSort(arr, sz); // calling heapsort to sort the array
printArray(arr, sz); // display array after sorting
assert(std::is_sorted(arr, arr + sz));
std::cout << "Test 1 Passed\n========================\n";
std::cout << "Test 2\n";
double arr2[] = {4.5, -3.6, 7.6, 0, 12.9};
sz = sizeof(arr2) / sizeof(arr2[0]);
printArray(arr2, sz);
heapSort(arr2, sz);
printArray(arr2, sz);
assert(std::is_sorted(arr2, arr2 + sz));
std::cout << "Test 2 passed\n";
}
/** Main function */
int main() {
int n;
std::cout << "Enter number of elements of array\n";
std::cin >> n;
int a[20];
for (int i = 0; i < n; ++i) {
std::cout << "Enter Element " << i << std::endl;
std::cin >> a[i];
}
build_maxheap(a, n);
heapsort(a, n);
std::cout << "Sorted Output\n";
for (int i = 0; i < n; ++i) {
std::cout << a[i] << std::endl;
}
std::getchar();
test();
return 0;
}

View File

@@ -1,36 +1,179 @@
// Insertion Sort
/**
*
* \file
* \brief [Insertion Sort Algorithm
* (Insertion Sort)](https://en.wikipedia.org/wiki/Insertion_sort)
*
* \details
* Insertion sort is a simple sorting algorithm that builds the final
* sorted array one at a time. It is much less efficient compared to
* other sorting algorithms like heap sort, merge sort or quick sort.
* However it has several advantages such as
* 1. Easy to implement
* 2. For small set of data it is quite efficient
* 3. More efficient that other Quadratic complexity algorithms like
* Selection sort or bubble sort.
* 4. It's stable that is it does not change the relative order of
* elements with equal keys
* 5. Works on hand means it can sort the array or list as it receives.
*
* It is based on the same idea that people use to sort the playing cards in
* their hands.
* the algorithms goes in the manner that we start iterating over the array
* of elements as soon as we find a unsorted element that is a misplaced
* element we place it at a sorted position.
*
* Example execution steps:
* 1. Suppose initially we have
* \f{bmatrix}{4 &3 &2 &5 &1\f}
* 2. We start traversing from 4 till we reach 1
* when we reach at 3 we find that it is misplaced so we take 3 and place
* it at a correct position thus the array will become
* \f{bmatrix}{3 &4 &2 &5 &1\f}
* 3. In the next iteration we are at 2 we find that this is also misplaced so
* we place it at the correct sorted position thus the array in this iteration
* becomes
* \f{bmatrix}{2 &3 &4 &5 &1\f}
* 4. We do not do anything with 5 and move on to the next iteration and
* select 1 which is misplaced and place it at correct position. Thus, we have
* \f{bmatrix}{1 &2 &3 &4 &5\f}
*/
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
int main() {
int n;
std::cout << "\nEnter the length of your array : ";
std::cin >> n;
int *Array = new int[n];
std::cout << "\nEnter any " << n << " Numbers for Unsorted Array : ";
// Input
for (int i = 0; i < n; i++) {
std::cin >> Array[i];
}
// Sorting
/** \namespace sorting
* \brief Sorting algorithms
*/
namespace sorting {
/** \brief
* Insertion Sort Function
*
* @tparam T type of array
* @param [in,out] arr Array to be sorted
* @param n Size of Array
*/
template <typename T>
void insertionSort(T *arr, int n) {
for (int i = 1; i < n; i++) {
int temp = Array[i];
T temp = arr[i];
int j = i - 1;
while (j >= 0 && temp < Array[j]) {
Array[j + 1] = Array[j];
while (j >= 0 && temp < arr[j]) {
arr[j + 1] = arr[j];
j--;
}
Array[j + 1] = temp;
arr[j + 1] = temp;
}
}
/** Insertion Sort Function
*
* @tparam T type of array
* @param [in,out] arr pointer to array to be sorted
*/
template <typename T>
void insertionSort(std::vector<T> *arr) {
size_t n = arr->size();
for (size_t i = 1; i < n; i++) {
T temp = arr[0][i];
int32_t j = i - 1;
while (j >= 0 && temp < arr[0][j]) {
arr[0][j + 1] = arr[0][j];
j--;
}
arr[0][j + 1] = temp;
}
}
} // namespace sorting
/**
* @brief Create a random array objecthelper function to create a random array
*
* @tparam T type of array
* @param arr array to fill (must be pre-allocated)
* @param N number of array elements
*/
template <typename T>
static void create_random_array(T *arr, int N) {
while (N--) {
double r = (std::rand() % 10000 - 5000) / 100.f;
arr[N] = static_cast<T>(r);
}
}
/** Test Cases to test algorithm */
void tests() {
int arr1[10] = {78, 34, 35, 6, 34, 56, 3, 56, 2, 4};
std::cout << "Test 1... ";
sorting::insertionSort(arr1, 10);
assert(std::is_sorted(arr1, arr1 + 10));
std::cout << "passed" << std::endl;
int arr2[5] = {5, -3, 7, -2, 1};
std::cout << "Test 2... ";
sorting::insertionSort(arr2, 5);
assert(std::is_sorted(arr2, arr2 + 5));
std::cout << "passed" << std::endl;
float arr3[5] = {5.6, -3.1, -3.0, -2.1, 1.8};
std::cout << "Test 3... ";
sorting::insertionSort(arr3, 5);
assert(std::is_sorted(arr3, arr3 + 5));
std::cout << "passed" << std::endl;
std::vector<float> arr4({5.6, -3.1, -3.0, -2.1, 1.8});
std::cout << "Test 4... ";
sorting::insertionSort(&arr4);
assert(std::is_sorted(std::begin(arr4), std::end(arr4)));
std::cout << "passed" << std::endl;
int arr5[50];
std::cout << "Test 5... ";
create_random_array(arr5, 50);
sorting::insertionSort(arr5, 50);
assert(std::is_sorted(arr5, arr5 + 50));
std::cout << "passed" << std::endl;
float arr6[50];
std::cout << "Test 6... ";
create_random_array(arr6, 50);
sorting::insertionSort(arr6, 50);
assert(std::is_sorted(arr6, arr6 + 50));
std::cout << "passed" << std::endl;
}
/** Main Function */
int main() {
/// Running predefined tests to test algorithm
tests();
/// For user insteraction
size_t n;
std::cout << "Enter the length of your array (0 to exit): ";
std::cin >> n;
if (n == 0) {
return 0;
}
// Output
int *arr = new int[n];
std::cout << "Enter any " << n << " Numbers for Unsorted Array : ";
for (int i = 0; i < n; i++) {
std::cin >> arr[i];
}
sorting::insertionSort(arr, n);
std::cout << "\nSorted Array : ";
for (int i = 0; i < n; i++) {
std::cout << Array[i] << "\t";
std::cout << arr[i] << " ";
}
delete[] Array;
std::cout << std::endl;
delete[] arr;
return 0;
}