diff --git a/.gitpod.dockerfile b/.gitpod.dockerfile index 53d336f81..f38914053 100644 --- a/.gitpod.dockerfile +++ b/.gitpod.dockerfile @@ -5,5 +5,7 @@ RUN sudo apt-get update \ doxygen \ graphviz \ ninja-build \ + freeglut3 \ + freeglut3-dev \ && pip install cpplint \ && sudo rm -rf /var/lib/apt/lists/* diff --git a/DIRECTORY.md b/DIRECTORY.md index 57a1b1c13..59c3a7e40 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -11,7 +11,10 @@ * [Sudoku Solve](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/backtracking/sudoku_solve.cpp) ## Ciphers + * [Caesar Cipher](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/ciphers/caesar_cipher.cpp) * [Hill Cipher](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/ciphers/hill_cipher.cpp) + * [Vigenere Cipher](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/ciphers/vigenere_cipher.cpp) + * [Xor Cipher](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/ciphers/xor_cipher.cpp) ## Data Structures * [Avltree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/avltree.cpp) @@ -33,6 +36,7 @@ * [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) @@ -215,12 +219,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) @@ -229,6 +235,7 @@ * [Numeric String Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/numeric_string_sort.cpp) * [Odd Even Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/odd_even_sort.cpp) * [Quick Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/quick_sort.cpp) + * [Quick Sort 3](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/quick_sort_3.cpp) * [Radix Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/radix_sort.cpp) * [Selection Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/selection_sort.cpp) * [Shell Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/shell_sort.cpp) diff --git a/ciphers/caesar_cipher.cpp b/ciphers/caesar_cipher.cpp new file mode 100644 index 000000000..6c5bba15f --- /dev/null +++ b/ciphers/caesar_cipher.cpp @@ -0,0 +1,124 @@ +/** + * @file caesar_cipher.cpp + * @brief Implementation of [Caesar cipher](https://en.wikipedia.org/wiki/Caesar_cipher) algorithm. + * + * @details + * In cryptography, a Caesar cipher, also known as Caesar's cipher, the shift cipher, + * Caesar's code or Caesar shift, is one of the simplest and most widely known encryption + * techniques. It is a type of substitution cipher in which each letter in the plaintext + * is replaced by a letter some fixed number of positions down the alphabet. For example, + * with a left shift of 3, D would be replaced by A, E would become B, and so on. + * The method is named after Julius Caesar, who used it in his private correspondence. + * + * ### Algorithm + * The encryption can also be represented using modular arithmetic by first transforming + * the letters into numbers, according to the scheme, A → 0, B → 1, ..., Z → 25. + * Encryption of a letter x by a shift n can be described mathematically as, + * \f[ E(x) = (x + n)\;\mbox{mod}\; 26\f] + * while decryption can be described as, + * \f[ D(x) = (x - n) \;\mbox{mod}\; 26\f] + * + * \note This program implements caesar cipher for only uppercase English alphabet characters (i.e. A-Z). + * + * @author [Deep Raval](https://github.com/imdeep2905) + */ +#include +#include +#include + +/** \namespace ciphers + * \brief Algorithms for encryption and decryption + */ +namespace ciphers { + /** \namespace caesar + * \brief Functions for [Caesar cipher](https://en.wikipedia.org/wiki/Caesar_cipher) algorithm. + */ + namespace caesar { + namespace { + /** + * This function finds character for given value (i.e.A-Z) + * @param x value for which we want character + * @returns corresponding character for perticular value + */ + inline char get_char(const int x) { + // By adding 65 we are scaling 0-25 to 65-90. + // Which are in fact ASCII values of A-Z. + return char(x + 65); + } + /** + * This function finds value for given character (i.e.0-25) + * @param c character for which we want value + * @returns returns corresponding value for perticular character + */ + inline int get_value(const char c) { + // A-Z have ASCII values in range 65-90. + // Hence subtracting 65 will scale them to 0-25. + return int(c - 65); + } + } // Unnamed namespace + /** + * Encrypt given text using caesar cipher. + * @param text text to be encrypted + * @param shift number of shifts to be applied + * @returns new encrypted text + */ + std::string encrypt (const std::string &text, const int &shift) { + std::string encrypted_text = ""; // Empty string to store encrypted text + for (char c : text) { // Going through each character + int place_value = get_value(c); // Getting value of character (i.e. 0-25) + place_value = (place_value + shift) % 26; // Applying encryption formula + char new_char = get_char(place_value); // Getting new character from new value (i.e. A-Z) + encrypted_text += new_char; // Appending encrypted character + } + return encrypted_text; // Returning encrypted text + } + /** + * Decrypt given text using caesar cipher. + * @param text text to be decrypted + * @param shift number of shifts to be applied + * @returns new decrypted text + */ + std::string decrypt (const std::string &text, const int &shift) { + std::string decrypted_text = ""; // Empty string to store decrypted text + for (char c : text) { // Going through each character + int place_value = get_value(c); // Getting value of character (i.e. 0-25) + place_value = (place_value - shift) % 26;// Applying decryption formula + if(place_value < 0) { // Handling case where remainder is negative + place_value = place_value + 26; + } + char new_char = get_char(place_value); // Getting original character from decrypted value (i.e. A-Z) + decrypted_text += new_char; // Appending decrypted character + } + return decrypted_text; // Returning decrypted text + } + } // namespace caesar +} // namespace ciphers + +/** + * Function to test above algorithm + */ +void test() { + // Test 1 + std::string text1 = "ALANTURING"; + std::string encrypted1 = ciphers::caesar::encrypt(text1, 17); + std::string decrypted1 = ciphers::caesar::decrypt(encrypted1, 17); + assert(text1 == decrypted1); + std::cout << "Original text : " << text1; + std::cout << " , Encrypted text (with shift = 21) : " << encrypted1; + std::cout << " , Decrypted text : "<< decrypted1 << std::endl; + // Test 2 + std::string text2 = "HELLOWORLD"; + std::string encrypted2 = ciphers::caesar::encrypt(text2, 1729); + std::string decrypted2 = ciphers::caesar::decrypt(encrypted2, 1729); + assert(text2 == decrypted2); + std::cout << "Original text : " << text2; + std::cout << " , Encrypted text (with shift = 1729) : " << encrypted2; + std::cout << " , Decrypted text : "<< decrypted2 << std::endl; +} + +/** Driver Code */ +int main() { + // Testing + test(); + return 0; +} diff --git a/ciphers/vigenere_cipher.cpp b/ciphers/vigenere_cipher.cpp new file mode 100644 index 000000000..4efd56c00 --- /dev/null +++ b/ciphers/vigenere_cipher.cpp @@ -0,0 +1,135 @@ +/** + * @file vigenere_cipher.cpp + * @brief Implementation of [Vigenère cipher](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher) algorithm. + * + * @details + * The Vigenère cipher is a method of encrypting alphabetic text by using a series of interwoven vigenere + * ciphers, based on the letters of a keyword. It employs a form of polyalphabetic substitution. + * + * ### Algorithm + * The encryption can also be represented using modular arithmetic by first transforming + * the letters into numbers, according to the scheme, A → 0, B → 1, ..., Z → 25. + * Encryption of \f$i^{th}\f$ character in Message M by key K can be described mathematically as, + * + * \f[ E_{K}(M_{i}) = (M_{i} + K_{i})\;\mbox{mod}\; 26\f] + * + * while decryption of \f$i^{th}\f$ character in Cipher C by key K can be described mathematically as, + * + * \f[ D_{k}(C_{i}) = (C_{i} - K_{i} + 26)\;\mbox{mod}\; 26\f] + * + * Where \f$K_{i}\f$ denotes corresponding character in key. If \f$|key| < |text|\f$ than + * same key is repeated untill their lengths are equal. + * + * For Example, + * If M = "ATTACKATDAWN" and K = "LEMON" than K becomes "LEMONLEMONLE". + * + * \note Rather than creating new key of equal length this program does this by using modular index for key + * (i.e. \f$(j + 1) \;\mbox{mod}\; |\mbox{key}|\f$) + * + * \note This program implements Vigenère cipher for only uppercase English alphabet characters (i.e. A-Z). + * + * @author [Deep Raval](https://github.com/imdeep2905) + */ +#include +#include +#include + +/** \namespace ciphers + * \brief Algorithms for encryption and decryption + */ +namespace ciphers { + /** \namespace vigenere + * \brief Functions for [vigenère cipher](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher) algorithm. + */ + namespace vigenere { + namespace { + /** + * This function finds character for given value (i.e.A-Z) + * @param x value for which we want character + * @return corresponding character for perticular value + */ + inline char get_char(const int x) { + // By adding 65 we are scaling 0-25 to 65-90. + // Which are in fact ASCII values of A-Z. + return char(x + 65); + } + /** + * This function finds value for given character (i.e.0-25) + * @param c character for which we want value + * @return returns corresponding value for perticular character + */ + inline int get_value(const char c) { + // A-Z have ASCII values in range 65-90. + // Hence subtracting 65 will scale them to 0-25. + return int(c - 65); + } + } // Unnamed namespace + /** + * Encrypt given text using vigenere cipher. + * @param text text to be encrypted + * @param key to be used for encryption + * @return new encrypted text + */ + std::string encrypt (const std::string &text, const std::string &key) { + std::string encrypted_text = ""; // Empty string to store encrypted text + // Going through each character of text and key + // Note that key is visited in circular way hence j = (j + 1) % |key| + for(size_t i = 0, j = 0; i < text.length(); i++, j = (j + 1) % key.length()) { + int place_value_text = get_value(text[i]); // Getting value of character in text + int place_value_key = get_value(key[j]); // Getting value of character in key + place_value_text = (place_value_text + place_value_key) % 26; // Applying encryption + char encrypted_char = get_char(place_value_text); // Getting new character from encrypted value + encrypted_text += encrypted_char; // Appending encrypted character + } + return encrypted_text; // Returning encrypted text + } + /** + * Decrypt given text using vigenere cipher. + * @param text text to be decrypted + * @param key key to be used for decryption + * @return new decrypted text + */ + std::string decrypt (const std::string &text, const std::string &key) { + // Going through each character of text and key + // Note that key is visited in circular way hence j = (j + 1) % |key| + std::string decrypted_text = ""; // Empty string to store decrypted text + for(size_t i = 0, j = 0; i < text.length(); i++, j = (j + 1) % key.length()) { + int place_value_text = get_value(text[i]); // Getting value of character in text + int place_value_key = get_value(key[j]); // Getting value of character in key + place_value_text = (place_value_text - place_value_key + 26) % 26; // Applying decryption + char decrypted_char = get_char(place_value_text); // Getting new character from decrypted value + decrypted_text += decrypted_char; // Appending decrypted character + } + return decrypted_text; // Returning decrypted text + } + } // namespace vigenere +} // namespace ciphers + +/** + * Function to test above algorithm + */ +void test() { + // Test 1 + std::string text1 = "NIKOLATESLA"; + std::string encrypted1 = ciphers::vigenere::encrypt(text1, "TESLA"); + std::string decrypted1 = ciphers::vigenere::decrypt(encrypted1, "TESLA"); + assert(text1 == decrypted1); + std::cout << "Original text : " << text1; + std::cout << " , Encrypted text (with key = TESLA) : " << encrypted1; + std::cout << " , Decrypted text : "<< decrypted1 << std::endl; + // Test 2 + std::string text2 = "GOOGLEIT"; + std::string encrypted2 = ciphers::vigenere::encrypt(text2, "REALLY"); + std::string decrypted2 = ciphers::vigenere::decrypt(encrypted2, "REALLY"); + assert(text2 == decrypted2); + std::cout << "Original text : " << text2; + std::cout << " , Encrypted text (with key = REALLY) : " << encrypted2; + std::cout << " , Decrypted text : "<< decrypted2 << std::endl; +} + +/** Driver Code */ +int main() { + // Testing + test(); + return 0; +} diff --git a/ciphers/xor_cipher.cpp b/ciphers/xor_cipher.cpp new file mode 100644 index 000000000..18cbdd61f --- /dev/null +++ b/ciphers/xor_cipher.cpp @@ -0,0 +1,99 @@ +/** + * @file xor_cipher.cpp + * @brief Implementation of [XOR cipher](https://en.wikipedia.org/wiki/XOR_cipher) algorithm. + * + * @details + * In cryptography, the simple XOR cipher is a type of additive cipher, an encryption + * algorithm that operates according to the principles: + * + * * \f$A {\oplus} 0 = A\f$ + * * \f$A {\oplus} A = 0\f$ + * * \f$ (A {\oplus} B) {\oplus} C = A {\oplus} (B {\oplus} C)\f$ + * * \f$ (B {\oplus} A) {\oplus} B = B {\oplus} 0 = B \f$ + * + * + * where \f$\oplus\f$ symbol denotes the exclusive disjunction (XOR) operation. + * This operation is sometimes called modulus 2 addition (or subtraction, which is identical). + * With this logic, a string of text can be encrypted by applying the bitwise XOR operator to + * every character using a given key. To decrypt the output, merely reapplying the XOR function + * with the key will remove the cipher. + * + * ### Algorithm + * Choose the key for encryption and apply XOR operation to each character of a string. + * Reapplying XOR operation to each character of encrypted string will give original string back. + * + * \note This program implements XOR Cipher for string with ASCII characters. + * + * @author [Deep Raval](https://github.com/imdeep2905) + */ +#include +#include +#include + +/** \namespace ciphers + * \brief Algorithms for encryption and decryption + */ +namespace ciphers { + /** \namespace XOR + * \brief Functions for [XOR cipher](https://en.wikipedia.org/wiki/XOR_cipher) algorithm. + */ + namespace XOR { + /** + * Encrypt given text using XOR cipher. + * @param text text to be encrypted + * @param key to be used for encyption + * @return new encrypted text + */ + std::string encrypt (const std::string &text, const int &key) { + std::string encrypted_text = ""; // Empty string to store encrypted text + for (auto &c: text) { // Going through each character + char encrypted_char = char(c ^ key); // Applying encyption + encrypted_text += encrypted_char; // Appending encrypted character + } + return encrypted_text; // Returning encrypted text + } + /** + * Decrypt given text using XOR cipher. + * @param text text to be encrypted + * @param key to be used for decryption + * @return new decrypted text + */ + std::string decrypt (const std::string &text, const int &key) { + std::string decrypted_text = ""; // Empty string to store decrypted text + for (auto &c : text) { // Going through each character + char decrypted_char = char(c ^ key); // Applying decryption + decrypted_text += decrypted_char; // Appending decrypted character + } + return decrypted_text; // Returning decrypted text + } + } // namespace XOR +} // namespace ciphers + +/** + * Function to test above algorithm + */ +void test() { + // Test 1 + std::string text1 = "Whipalsh! : Do watch this movie..."; + std::string encrypted1 = ciphers::XOR::encrypt(text1, 17); + std::string decrypted1 = ciphers::XOR::decrypt(encrypted1, 17); + assert(text1 == decrypted1); + std::cout << "Original text : " << text1; + std::cout << " , Encrypted text (with key = 17) : " << encrypted1; + std::cout << " , Decrypted text : "<< decrypted1 << std::endl; + // Test 2 + std::string text2 = "->Valar M0rghulis<-"; + std::string encrypted2 = ciphers::XOR::encrypt(text2, 29); + std::string decrypted2 = ciphers::XOR::decrypt(encrypted2, 29); + assert(text2 == decrypted2); + std::cout << "Original text : " << text2; + std::cout << " , Encrypted text (with key = 29) : " << encrypted2; + std::cout << " , Decrypted text : "<< decrypted2 << std::endl; +} + +/** Driver Code */ +int main() { + // Testing + test(); + return 0; +} diff --git a/data_structures/queue_using_two_stacks.cpp b/data_structures/queue_using_two_stacks.cpp new file mode 100644 index 000000000..a09644686 --- /dev/null +++ b/data_structures/queue_using_two_stacks.cpp @@ -0,0 +1,144 @@ +/** + * @author [shoniavika](https://github.com/shoniavika) + * @file + * + * Implementation of a Queue using two Stacks. + */ + +#include +#include +#include + +namespace { +/** + * @brief Queue data structure. Stores elements in FIFO + * (first-in-first-out) manner. + * @tparam T datatype to store in the queue + */ +template +class MyQueue { + private: + std::stack 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 +void MyQueue::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 +const T& MyQueue::pop() { + const T& temp = MyQueue::peek(); + s2.pop(); + return temp; +} + +/** + * Returns element in the front. + * Does not remove it. + */ +template +const T& MyQueue::peek() const { + if (!empty()) { + return s2.top(); + } + std::cerr << "Queue is empty" << std::endl; + exit(0); +} + +/** + * Checks whether a queue is empty + */ +template +bool MyQueue::empty() const { + return s2.empty() && s1.empty(); +} +} // namespace + +/** + * Testing function + */ +void queue_test() { + MyQueue 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 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; +} diff --git a/math/least_common_multiple.cpp b/math/least_common_multiple.cpp index 741945716..e9e46490a 100644 --- a/math/least_common_multiple.cpp +++ b/math/least_common_multiple.cpp @@ -26,12 +26,12 @@ unsigned int gcd(unsigned int x, unsigned int y) { if (x > y) { // The following is valid because we have checked whether y == 0 - int temp = x / y; + unsigned int temp = x / y; return gcd(y, x - temp * y); } // Again the following is valid because we have checked whether x == 0 - int temp = y / x; + unsigned int temp = y / x; return gcd(x, y - temp * x); } @@ -40,7 +40,9 @@ unsigned int gcd(unsigned int x, unsigned int y) { * @params integer x and y whose lcm we want to find. * @return lcm of x and y using the relation x * y = gcd(x, y) * lcm(x, y) */ -unsigned int lcm(unsigned int x, unsigned int y) { return x * y / gcd(x, y); } +unsigned int lcm(unsigned int x, unsigned int y) { + return x / gcd(x, y) * y; +} /** * Function for testing the lcm() functions with some assert statements. @@ -59,6 +61,15 @@ void tests() { lcm(2, 3) == 6)); std::cout << "Second assertion passes: LCM of 2 and 3 is " << lcm(2, 3) << std::endl; + + // Testing an integer overflow. + // The algorithm should work as long as the result fits into integer. + assert(((void)"LCM of 987654321 and 987654321 is 987654321 but lcm function" + " gives a different result.\n", + lcm(987654321, 987654321) == 987654321)); + std::cout << "Third assertion passes: LCM of 987654321 and 987654321 is " + << lcm(987654321, 987654321) + << std::endl; } /** diff --git a/math/sieve_of_eratosthenes.cpp b/math/sieve_of_eratosthenes.cpp index e30bc1891..e011b6c00 100644 --- a/math/sieve_of_eratosthenes.cpp +++ b/math/sieve_of_eratosthenes.cpp @@ -1,58 +1,72 @@ /** * @file * @brief Get list of prime numbers using Sieve of Eratosthenes - * Sieve of Eratosthenes is an algorithm to find the primes - * that is between 2 to N (as defined in main). + * @details + * Sieve of Eratosthenes is an algorithm that finds all the primes + * between 2 and N. * - * Time Complexity : \f$O(N \cdot\log N)\f$ + * Time Complexity : \f$O(N \cdot\log \log N)\f$ *
Space Complexity : \f$O(N)\f$ * * @see primes_up_to_billion.cpp prime_numbers.cpp */ -#include // for io operations +#include +#include +#include /** - * This is the function that finds the primes and eliminates - * the multiples. + * This is the function that finds the primes and eliminates the multiples. + * Contains a common optimization to start eliminating multiples of + * a prime p starting from p * p since all of the lower multiples + * have been already eliminated. * @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 + * @return is_prime a vector of `N + 1` booleans identifying if `i`^th number is a prime or not */ -void sieve(uint32_t N, bool *isprime) { - isprime[0] = true; - isprime[1] = true; +std::vector sieve(uint32_t N) { + std::vector is_prime(N + 1, true); + is_prime[0] = is_prime[1] = false; 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; + if (is_prime[i]) { + for (uint32_t j = i * i; j <= N; j += i) { + is_prime[j] = false; } } } + return is_prime; } /** * 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 + * @param is_prime a vector of `N + 1` booleans identifying if `i`^th number is a prime or not */ -void print(uint32_t N, const bool *isprime) { +void print(uint32_t N, const std::vector &is_prime) { for (uint32_t i = 2; i <= N; i++) { - if (!isprime[i]) { + if (is_prime[i]) { std::cout << i << ' '; } } std::cout << std::endl; } +/** + * Test implementations + */ +void tests() { + // 0 1 2 3 4 5 6 7 8 9 10 + std::vector ans{false, false, true, true, false, true, false, true, false, false, false}; + assert(sieve(10) == ans); +} + /** * Main function */ int main() { - uint32_t N = 100; - bool *isprime = new bool[N]; - sieve(N, isprime); - print(N, isprime); - delete[] isprime; + tests(); + uint32_t N = 100; + std::vector is_prime = sieve(N); + print(N, is_prime); return 0; } diff --git a/sorting/bogo_sort.cpp b/sorting/bogo_sort.cpp new file mode 100644 index 000000000..62fbef48e --- /dev/null +++ b/sorting/bogo_sort.cpp @@ -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. + * + * @author [Deep Raval](https://github.com/imdeep2905) + */ +#include +#include +#include +#include + + +/** + * @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 +std::array shuffle (std::array 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 +std::array randomized_bogosort (std::array 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 +void show_array (const std::array &arr) { + for (int x : arr) { + std::cout << x << ' '; + } + std::cout << '\n'; +} + +/** + * Function to test above algorithm + */ +void test() { + // Test 1 + std::array 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 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 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; +} diff --git a/sorting/gnome_sort.cpp b/sorting/gnome_sort.cpp new file mode 100644 index 000000000..d56662479 --- /dev/null +++ b/sorting/gnome_sort.cpp @@ -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 // for std::swap +#include // for std::array +#include // for assertions +#include // 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 +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 +std::array gnomeSort(std::array 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 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_arr = {-100.2, 10.2, 20.0, 9.0, 7.5, 7.2}; + std::array 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 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_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; +} diff --git a/sorting/quick_sort_3.cpp b/sorting/quick_sort_3.cpp new file mode 100644 index 000000000..b365144a1 --- /dev/null +++ b/sorting/quick_sort_3.cpp @@ -0,0 +1,188 @@ +/** + * @file + * @brief Implementation Details + * @details Quick sort 3 works on Dutch National Flag Algorithm + * The major difference between simple quicksort and quick sort 3 comes in the + * function partition3 In quick_sort_partition3 we divide the vector/array into + * 3 parts. quick sort 3 works faster in some cases as compared to simple + * quicksort. + * @author immortal-j + * @author [Krishna Vedala](https://github/kvedala) + */ +#include +#include +#include +#include +#include + +namespace { +/** + * Operator to print the array. + * @param out std::ostream object to write to + * @param arr array to write + */ +template +std::ostream &operator<<(std::ostream &out, const std::vector &arr) { + for (size_t i = 0; i < arr.size(); ++i) { + out << arr[i]; + if (i < arr.size() - 1) { + out << ", "; + } + } + return out; +} + +} // namespace + +/** + * @namespace sorting + * @brief Sorting Algorithms + */ +namespace sorting { +namespace { // using un-named namespace here to prevent partition function + // being visible to end-users +/** This function partitions `arr[]` in three parts + * 1. \f$arr[l\ldots i]\f$ contains all elements smaller than pivot + * 2. \f$arr[(i+1)\ldots (j-1)]\f$ contains all occurrences of pivot + * 3. \f$arr[j\ldots r]\f$ contains all elements greater than pivot + * @tparam T type of data in the vector array + * @param [in,out] arr vector array being partitioned + * @param [in] low lower limit of window to partition + * @param [in] high upper limit of window to partition + * @param [out] i updated lower limit of partition + * @param [out] j updated upper limit of partition + */ +template +void partition3(std::vector *arr, int32_t low, int32_t high, int32_t *i, + int32_t *j) { + // To handle 2 elements + if (high - low <= 1) { + if ((*arr)[high] < (*arr)[low]) { + std::swap((*arr)[high], (*arr)[low]); + } + *i = low; + *j = high; + return; + } + + int32_t mid = low; + T pivot = (*arr)[high]; + while (mid <= high) { + if ((*arr)[mid] < pivot) { + std::swap((*arr)[low++], (*arr)[mid++]); + } else if ((*arr)[mid] == pivot) { + mid++; + } else if ((*arr)[mid] > pivot) { + std::swap((*arr)[mid], (*arr)[high--]); + } + } + + // update i and j + *i = low - 1; + *j = mid; // or high-1 +} +} // namespace + +/** 3-way partition based quick sort. This function accepts array pointer and + * modified the input array. + * @tparam T type of data in the vector array + * @param [in,out] arr vector array to sort + * @param [in] low lower limit of window to partition + * @param [in] high upper limit of window to partition + */ +template +void quicksort(std::vector *arr, int32_t low, int32_t high) { + if (low >= high) { // 1 or 0 elements + return; + } + + int32_t i = 0, j = 0; + + // i and j are passed as reference + partition3(arr, low, high, &i, &j); + + // Recur two halves + quicksort(arr, low, i); + quicksort(arr, j, high); +} + +/** 3-way partition based quick sort. This function accepts array by value and + * creates a copy of it. The array copy gets sorted and returned by the + * function. + * @tparam T type of data in the vector array + * @param [in] arr vector array to sort + * @param [in] low lower limit of window to partition + * @param [in] high upper limit of window to partition + * @returns sorted array vector + */ +template +std::vector quicksort(std::vector arr, int32_t low, int32_t high) { + if (low >= high) { // 1 or 0 elements + return arr; + } + + int32_t i = 0, j = 0; + + // i and j are passed as reference + partition3(&arr, low, high, &i, &j); + + // Recur two halves + quicksort(&arr, low, i); + quicksort(&arr, j, high); + + return arr; +} +} // namespace sorting + +/** Test function for integer type arrays */ +static void test_int() { + std::cout << "\nTesting integer type arrays\n"; + + for (int num_tests = 1; num_tests < 21; num_tests++) { + size_t size = std::rand() % 500; + std::vector arr(size); + for (auto &a : arr) { + a = std::rand() % 500 - 250; // random numbers between -250, 249 + } + + std::cout << "Test " << num_tests << "\t Array size:" << size << "\t "; + std::vector sorted = sorting::quicksort(arr, 0, size - 1); + if (size < 20) { + std::cout << "\t Sorted Array is:\n\t"; + std::cout << sorted << "\n"; + } + assert(std::is_sorted(std::begin(sorted), std::end(sorted))); + std::cout << "\t Passed\n"; + } +} + +/** Test function for double type arrays */ +static void test_double() { + std::cout << "\nTesting Double type arrays\n"; + for (int num_tests = 1; num_tests < 21; num_tests++) { + size_t size = std::rand() % 500; + std::vector arr(size); + for (auto &a : arr) { + a = double(std::rand() % 500) - + 250.f; // random numbers between -250, 249 + a /= 100.f; // convert to -2.5 to 2.49 + } + + std::cout << "Test " << num_tests << "\t Array size:" << size << "\t "; + std::vector sorted = sorting::quicksort(arr, 0, size - 1); + if (size < 20) { + std::cout << "\t Sorted Array is:\n\t"; + std::cout << sorted << "\n"; + } + assert(std::is_sorted(std::begin(sorted), std::end(sorted))); + std::cout << "\t Passed\n"; + } +} + +/** Driver program for above functions */ +int main() { + std::srand(std::time(nullptr)); + test_int(); + test_double(); + return 0; +}