├── branding └── logo.png ├── .gitignore ├── README.md ├── src ├── math │ ├── ceil.d │ └── math_parser.d ├── sort │ ├── stalin_sort.d │ ├── bubble_sort.d │ ├── insert_sort.d │ └── selection_sort.d ├── problems │ ├── codewars │ │ ├── primes_in_numbers.d │ │ ├── counting_duplicates.d │ │ ├── human_readable_duration_format.d │ │ └── screen_locking_patterns.d │ └── geekforgeek │ │ └── majority_element_2.d ├── dynamic_programming │ └── cut_rod.d ├── compilers │ └── brainfuck.d └── data_structures │ └── binary_heap.d └── LICENSE /branding/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlangalgorithms/Algorithms/HEAD/branding/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.o 3 | *.obj 4 | 5 | # Compiled Dynamic libraries 6 | *.so 7 | *.dylib 8 | *.dll 9 | 10 | # Compiled Static libraries 11 | *.a 12 | *.lib 13 | 14 | # Executables 15 | *.exe 16 | 17 | # DUB 18 | .dub 19 | docs.json 20 | __dummy.html 21 | docs/ 22 | 23 | # Code coverage 24 | *.lst 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |

Algorithms

5 | All algorithms implemented in D 6 |
7 | 8 | >[!NOTE] 9 | > This repository is designed for beginners who want to get acquainted with the D language, but it can be useful for professionals as well. 10 | 11 | The repository contains a lot of algorithms, 12 | from the simplest to the most complex. 13 | 14 | Here you can find everything from bubble sort 15 | to a compiler for your own language. 16 | 17 | If you want, you can contribute to the project. 18 | 19 | Even if you are new to D, but can write simple algorithms, 20 | you can find an interesting algorithm, implement it and add it here! -------------------------------------------------------------------------------- /src/math/ceil.d: -------------------------------------------------------------------------------- 1 | import std; 2 | 3 | /** 4 | * Returns the smallest (closest to negative infinity) 5 | * 6 | * @param number the number 7 | * @return the smallest (closest to negative infinity) of given 8 | * {@code number} 9 | */ 10 | 11 | double ceil(double number) { 12 | if (number - cast(int) number == 0) { 13 | return number; 14 | } else if (number - cast(int) number > 0) { 15 | return cast(int)(number + 1); 16 | } else { 17 | return cast(int) number; 18 | } 19 | } 20 | 21 | unittest { 22 | 23 | assert(ceil(10.2) == 11); 24 | assert(ceil(9) == 9); 25 | assert(ceil(12.6) == 13); 26 | 27 | } 28 | 29 | /*Program tests are performed above in the unittest block*/ 30 | int main(){ 31 | return 0; 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Dlang Algorithms 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/sort/stalin_sort.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | /* 4 | * Stalin Sort is a humorous sorting algorithm that modifies the input array in-place. 5 | * It keeps elements that are in non-decreasing order and removes elements that break this order. 6 | * 7 | * 1) Start with the first element of the array as the base. 8 | * 2) Iterate through the array, keeping only elements that are greater than or equal to the last kept element. 9 | * 3) Shift all valid elements to the left, effectively removing invalid ones. 10 | * 4) Resize the array to include only valid elements. 11 | * 12 | * @param arr the array of integers to be sorted 13 | */ 14 | void stalin_sort(ref ulong[] arr) { 15 | if (arr.length == 0) return; // No action for an empty array 16 | 17 | ulong index = 0; // Position to keep the current element 18 | 19 | for (ulong i = 1; i < arr.length; i++) { 20 | if (arr[i] >= arr[index]) { 21 | index++; 22 | arr[index] = arr[i]; // Keep valid element in place 23 | } 24 | } 25 | 26 | arr.length = index + 1; // Truncate the array to contain only valid elements 27 | } 28 | 29 | unittest{ 30 | ulong[] data = [64, 34, 25, 12, 22, 11, 90]; 31 | stalin_sort(data); 32 | assert(data == [64,90]); 33 | } 34 | 35 | void main() {} -------------------------------------------------------------------------------- /src/sort/bubble_sort.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | /** 4 | * Sorts an array of integers in ascending order using the Bubble Sort algorithm. 5 | * 6 | * This algorithm repeatedly steps through the list, compares adjacent elements, 7 | * and swaps them if they are in the wrong order. The pass through the list is 8 | * repeated until the list is sorted. 9 | * 10 | * @param arr the array of integers to be sorted 11 | */ 12 | void bubbleSort(int[] arr) { 13 | // Get the length of the array 14 | ulong n = arr.length; 15 | 16 | // Outer loop for each element in the array, except the last one 17 | for (int i = 0; i < n - 1; i++) { 18 | // Inner loop for comparing adjacent elements 19 | // The last i elements are already sorted, so we reduce the range 20 | for (int j = 0; j < n - 1 - i; j++) { 21 | // If the current element is greater than the next element, swap them 22 | if (arr[j] > arr[j + 1]) { 23 | // Temporary variable to hold the current element during swap 24 | int temp = arr[j]; 25 | arr[j] = arr[j + 1]; // Assign next element to current position 26 | arr[j + 1] = temp; // Assign the temporary variable to next position 27 | } 28 | } 29 | } 30 | } 31 | 32 | /* 33 | TESTS 34 | */ 35 | unittest{ 36 | int[] data = [64, 34, 25, 12, 22, 11, 90]; 37 | bubbleSort(data); 38 | assert(data == [11,12,22,25,34,64,90]); 39 | } 40 | 41 | 42 | void main() {} -------------------------------------------------------------------------------- /src/sort/insert_sort.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | /* 4 | * Insertion sort is a simple sorting algorithm that works by iteratively inserting each element of an unsorted list into its correct position in a sorted portion of the list. 5 | * It is like sorting playing cards in your hands. You split the cards into two groups: the sorted cards and the unsorted cards. 6 | * Then, you pick a card from the unsorted group and put it in the right place in the sorted group. 7 | * 8 | * 1) We start with second element of the array as first element in the array is assumed to be sorted. 9 | * 2) Compare second element with the first element and check if the second element is smaller then swap them. 10 | * 3) Move to the third element and compare it with the first two elements and put at its correct position 11 | * 4) Repeat until the entire array is sorted. 12 | * 13 | * @param arr the array of integers to be sorted 14 | */ 15 | void insert_sort(ulong[] arr) { 16 | for (int i = 1; i < arr.length; i++) { 17 | ulong key = arr[i]; 18 | int j = i - 1; 19 | // Move elements of arr[0..i-1], that are greater than key, to one position ahead of their current position 20 | while (j >= 0 && key < arr[j]) { 21 | arr[j + 1] = arr[j]; 22 | j--; 23 | } 24 | arr[j + 1] = key; 25 | } 26 | } 27 | 28 | unittest{ 29 | ulong[] data = [64, 34, 25, 12, 22, 11, 90]; 30 | insert_sort(data); 31 | assert(data == [11,12,22,25,34,64,90]); 32 | } 33 | 34 | void main() {} -------------------------------------------------------------------------------- /src/sort/selection_sort.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | /* 4 | * Selection Sort is a comparison-based sorting algorithm. 5 | * It sorts an array by repeatedly selecting the smallest (or largest) element from the unsorted portion and swapping it with the first unsorted element. 6 | * This process continues until the entire array is sorted. 7 | * 8 | * 1) First we find the smallest element and swap it with the first element. This way we get the smallest element at its correct position. 9 | * 2) Then we find the smallest among remaining elements (or second smallest) and move it to its correct position by swapping. 10 | * 3) We keep doing this until we get all elements moved to correct position. 11 | * 12 | * @param arr the array of integers to be sorted 13 | */ 14 | void selection_sort(ulong[] arr) { 15 | // Get the length of the array 16 | ulong n = arr.length; 17 | 18 | for (ulong i = 0; i < n-1; i++) { 19 | // Assume the current position holds the minimum element 20 | ulong min_index = i; 21 | 22 | // Iterate through the unsorted portion to find the actual minimum 23 | for (ulong j = i + 1; j < n; j++) { 24 | // Update min_idx if a smaller element is found 25 | if (arr[j] < arr[min_index]) min_index = j; 26 | } 27 | 28 | // Move minimum element to its correct position 29 | ulong temp = arr[i]; 30 | arr[i] = arr[min_index]; 31 | arr[min_index] = temp; 32 | } 33 | } 34 | 35 | unittest{ 36 | ulong[] data = [64, 34, 25, 12, 22, 11, 90]; 37 | selection_sort(data); 38 | assert(data == [11,12,22,25,34,64,90]); 39 | } 40 | 41 | void main() {} -------------------------------------------------------------------------------- /src/problems/codewars/primes_in_numbers.d: -------------------------------------------------------------------------------- 1 | import std.format; 2 | import std.math : sqrt; 3 | import std.algorithm : sort; 4 | 5 | /* 6 | * Link: https://www.codewars.com/kata/54d512e62a5e54c96200019e 7 | * 8 | * Primes in numbers 9 | * Difficulty: 5 kyu 10 | * 11 | * Problem: 12 | * Given a positive number n > 1 find the prime factor decomposition of n. 13 | * The result will be a string with the following form: "(p1**n1)(p2**n2)...(pk**nk)" 14 | * 15 | * With the p(i) in increasing order and n(i) empty if n(i) is 1. 16 | * 17 | * Example: 18 | * n = 86240 should return "(2**5)(5)(7**2)(11)" 19 | * 20 | * Tags: Fundamentals, Mathematics 21 | * 22 | */ 23 | string primeFactors(long n) { 24 | string result; 25 | int count = 0; 26 | 27 | while (n % 2 == 0) { 28 | n /= 2; 29 | count++; 30 | } 31 | 32 | if (count > 0) result ~= (count == 1) ? "(2)" : format("(2**%d)", count); 33 | 34 | for (int i = 3; i < (sqrt(cast(float)n) + 1); i+=2) { 35 | count = 0; 36 | while (n % i == 0) { 37 | n /= i; 38 | count++; 39 | } 40 | if (count > 0) result ~= (count == 1) ? format("(%d)", i) : format("(%d**%d)", i, count); 41 | } 42 | 43 | if (n > 2) { 44 | result ~= format("(%d)", n); 45 | } 46 | 47 | return result; 48 | } 49 | 50 | unittest { 51 | assert(primeFactors(7775460) == "(2**2)(3**3)(5)(7)(11**2)(17)"); 52 | assert(primeFactors(7919) == "(7919)"); 53 | assert(primeFactors(17*17*93*677) == "(3)(17**2)(31)(677)"); 54 | assert(primeFactors(933555431) == "(7537)(123863)"); 55 | } 56 | 57 | void main() { 58 | 59 | } -------------------------------------------------------------------------------- /src/problems/codewars/counting_duplicates.d: -------------------------------------------------------------------------------- 1 | import std; 2 | import std.uni : toLower; 3 | /* 4 | * Link: https://www.codewars.com/kata/54bf1c2cd5b56cc47f0007a1 5 | * 6 | * Counting Duplicates 7 | * Difficulty: 6 kyu 8 | * 9 | * Problem: 10 | * Count the number of Duplicates 11 | * Write a function that will return the count of distinct case-insensitive alphabetic characters and numeric digits that occur more than once in the input string. 12 | * The input string can be assumed to contain only alphabets (both uppercase and lowercase) and numeric digits. 13 | * 14 | * Examples 15 | * "abcde" -> 0 # no characters repeats more than once 16 | * "aabbcde" -> 2 # 'a' and 'b' 17 | * "aabBcde" -> 2 # 'a' occurs twice and 'b' twice (`b` and `B`) 18 | * "indivisibility" -> 1 # 'i' occurs six times 19 | * "Indivisibilities" -> 2 # 'i' occurs seven times and 's' occurs twice 20 | * "aA11" -> 2 # 'a' and '1' 21 | * "ABBA" -> 2 # 'A' and 'B' each occur twice 22 | * 23 | * Tags: Strings, Fundamentals 24 | * 25 | */ 26 | 27 | /* 28 | * Returns amount of duplicated chars in the text. 29 | * 30 | * @param text just text 31 | * @return amount of duplicated chars 32 | * {@code text} 33 | */ 34 | uint duplicate_count(string text) { 35 | text = toLower(text); 36 | int[char] map; 37 | int result = 0; 38 | 39 | foreach (c; text) { 40 | if (c !in map) map[c] = 0; 41 | map[c]++; 42 | } 43 | 44 | foreach (key, value; map) { 45 | if (value > 1) result++; 46 | } 47 | 48 | return result; 49 | } 50 | 51 | unittest { 52 | assert(duplicate_count("abcde") == 0); 53 | assert(duplicate_count("aabbcde") == 2); 54 | assert(duplicate_count("aabBcde") == 2); 55 | assert(duplicate_count("indivisibility") == 1); 56 | assert(duplicate_count("Indivisibilities") == 2); 57 | assert(duplicate_count("aA11") == 2); 58 | assert(duplicate_count("ABBA") == 2); 59 | } 60 | 61 | // Program tests are performed above in the unittest block 62 | int main(){ 63 | return 0; 64 | } -------------------------------------------------------------------------------- /src/dynamic_programming/cut_rod.d: -------------------------------------------------------------------------------- 1 | import std; 2 | 3 | /** 4 | * Calculates the maximum profit that can be obtained by cutting a rod 5 | * of length n and selling the pieces based on the given price array. 6 | * 7 | * @param price an array of integers representing the prices for each 8 | * length of the rod (1-indexed, where price[i-1] is the 9 | * price for a rod of length i). 10 | * @param n the total length of the rod. 11 | * @return the maximum profit that can be obtained by cutting the rod. 12 | */ 13 | int maxProfitByCuttingRod(int[] price, uint64_t n) { 14 | // Array to store maximum profits for each length of the rod 15 | int[] profit = new int[n + 1]; 16 | 17 | // Base case: No profit can be made from a rod of length 0 18 | profit[0] = 0; 19 | 20 | // Iterate over lengths from 1 to n 21 | for (size_t i = 1; i <= n; i++) { 22 | int q = int.min; // Initialize maximum profit for this length 23 | 24 | // Check all possible cuts of the rod 25 | for (size_t j = 1; j <= i; j++) { 26 | // Calculate profit by making a cut at length j 27 | q = max(q, price[j - 1] + profit[i - j]); 28 | } 29 | 30 | // Store the maximum profit for rod length i 31 | profit[i] = q; 32 | } 33 | 34 | // Return the maximum profit for the full length of the rod 35 | return profit[n]; 36 | } 37 | 38 | /** 39 | TESTS 40 | */ 41 | 42 | unittest { 43 | // Test case to validate the maxProfitByCuttingRod function 44 | const int n1 = 8; // Length of the rod 45 | int[] price1 = [1 , 5, 8, 9, 10, 17 , 17 , 20]; // Prices for each length 46 | const int max_profit1 = maxProfitByCuttingRod(price1, n1); // Calculate max profit 47 | const int expected_max_profit1 = 22; // Expected result 48 | assert(max_profit1 == expected_max_profit1); // Assert that calculated profit matches expected 49 | writeln(max_profit1); // Output result 50 | } 51 | 52 | /** 53 | Program tests are performed above in the unittest block 54 | */ 55 | void main() {} 56 | -------------------------------------------------------------------------------- /src/compilers/brainfuck.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | import std.file; 3 | import std.string; 4 | 5 | void main(string[] args) { 6 | if (args.length < 2) { 7 | writeln("Usage: ", args[0], " filename"); 8 | return; 9 | } 10 | 11 | auto inputFile = args[1]; 12 | auto outputFile = inputFile[0 .. inputFile.length - inputFile.lastIndexOf('.')].dup ~ ".txt"; 13 | 14 | try { 15 | auto content = readText(inputFile); 16 | auto writer = File(outputFile, "w"); 17 | 18 | writer.write("#include \nint main(){unsigned char tape[30000] = { 0 };unsigned char* ptr = tape;\n"); 19 | 20 | int i = 0; 21 | while (i < content.length) { 22 | char ch = content[i]; 23 | int sameCh = 1; 24 | 25 | while (i + sameCh < content.length && content[i + sameCh] == ch) { 26 | sameCh++; 27 | } 28 | 29 | final switch (ch) { 30 | case '>': 31 | writer.write("ptr+=", sameCh, ";\n"); 32 | break; 33 | case '<': 34 | writer.write("ptr-=", sameCh, ";\n"); 35 | break; 36 | case '+': 37 | writer.write("*ptr+=", sameCh, ";\n"); 38 | break; 39 | case '-': 40 | writer.write("*ptr-=", sameCh, ";\n"); 41 | break; 42 | case '.': 43 | writer.write("putchar(*ptr);\n"); 44 | break; 45 | case ',': 46 | writer.write("*ptr=getchar();\n"); 47 | break; 48 | case '[': 49 | writer.write("while(*ptr){\n"); 50 | break; 51 | case ']': 52 | writer.write("}\n"); 53 | break; 54 | } 55 | 56 | i += sameCh; 57 | } 58 | 59 | writer.write("}\n"); 60 | writer.close(); 61 | } catch (Exception e) { 62 | writeln("File not found or an error occurred: ", e.msg); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/problems/geekforgeek/majority_element_2.d: -------------------------------------------------------------------------------- 1 | import std; 2 | import std.algorithm : sort; 3 | /* 4 | * Link: https://www.geeksforgeeks.org/problems/majority-vote/1 5 | * 6 | * Majority Element II 7 | * Difficulty: Medium 8 | * 9 | * Problem: 10 | * You are given an array of integer int[] where each number represents a vote to a candidate. 11 | * Return the candidates that have votes greater than one-third of the total votes, If there's not a majority vote, return an empty array. 12 | * 13 | * Note: The answer should be returned in an increasing format. 14 | * 15 | * Examples: 16 | * ==================================================== 17 | * Input: int[] array = [2, 1, 5, 5, 5, 5, 6, 6, 6, 6, 6]; 18 | * Output: [5, 6] 19 | * Explanation: 5 and 6 occur more n/3 times. 20 | * ==================================================== 21 | * 22 | * ==================================================== 23 | * Input: int[] array = [1, 2, 3, 4, 5]; 24 | * Output: [] 25 | * Explanation: no candidate occur more than n/3 times. 26 | * ==================================================== 27 | * 28 | * Constraint: 29 | * 1 <= arr.size() <= 10^6 30 | * -10^9 <= arr[i] <= 10^9 31 | * 32 | */ 33 | 34 | /* 35 | * Returns an array of major numbers where each number represents a candidate whos amount of votes greater than one-third of the total votes. 36 | * 37 | * @param array array of votes 38 | * @return an array of major numbers 39 | * {@code array} 40 | */ 41 | int[] find_majority(int[] array) { 42 | int[int] counter = new int[int]; 43 | 44 | foreach(int num; array) { 45 | if (num in counter) { 46 | counter[num]++; 47 | } else { 48 | counter[num] = 1; 49 | } 50 | } 51 | 52 | int[] result = []; 53 | foreach(key, value; counter) { 54 | if (value > array.length / 3) result ~= key; 55 | } 56 | 57 | sort(result); // The answer should be returned in an increasing format. 58 | return result; 59 | } 60 | 61 | unittest { 62 | assert(find_majority([2, 1, 5, 5, 5, 5, 6, 6, 6, 6, 6]) == [5, 6]); 63 | assert(find_majority([1, 2, 3, 4, 5]) == []); 64 | assert(find_majority([1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 6, 6, 6, 7, 8]) == [2]); 65 | assert(find_majority([1]) == [1]); 66 | assert(find_majority([1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]) == []); 67 | } 68 | 69 | // Program tests are performed above in the unittest block 70 | int main(){ 71 | return 0; 72 | } -------------------------------------------------------------------------------- /src/problems/codewars/human_readable_duration_format.d: -------------------------------------------------------------------------------- 1 | import std; 2 | import std.conv; 3 | 4 | /* 5 | * Link: https://www.codewars.com/kata/52742f58faf5485cae000b9a 6 | * 7 | * Human readable duration format 8 | * Difficulty: 4 kyu 9 | * 10 | * Problem: 11 | * Your task in order to complete this Kata is to write a function which formats a duration, given as a number of seconds, in a human-friendly way. 12 | * 13 | * The function must accept a non-negative integer. If it is zero, it just returns `"now"`. 14 | * Otherwise, the duration is expressed as a combination of `years`, `days`, `hours`, `minutes` and `seconds`. 15 | * 16 | * It is much easier to understand with an example: 17 | * 18 | * For seconds = 62, your function should return 19 | * "1 minute and 2 seconds" 20 | * For seconds = 3662, your function should return 21 | * "1 hour, 1 minute and 2 seconds" 22 | * 23 | * For the purpose of this Kata, a year is 365 days and a day is 24 hours. 24 | * 25 | * Note that spaces are important. 26 | * 27 | * Detailed rules 28 | * 29 | * The resulting expression is made of components like `4 seconds`, `1 year`, etc. 30 | * In general, a positive integer and one of the valid units of time, separated by a space. 31 | * The unit of time is used in plural if the integer is greater than 1. 32 | * 33 | * The components are separated by a comma and a space (`", "`). 34 | * Except the last component, which is separated by `" and "`, just like it would be written in English. 35 | * 36 | * A more significant units of time will occur before than a least significant one. 37 | * Therefore, `1 second and 1 year` is not correct, but `1 year and 1 second` is. 38 | * 39 | * Different components have different unit of times. 40 | * So there is not repeated units like in `5 seconds and 1 second`. 41 | * 42 | * A component will not appear at all if its value happens to be zero. 43 | * Hence, `1 minute and 0 seconds` is not valid, but it should be just `1 minute`. 44 | * 45 | * A unit of time must be used "as much as possible". 46 | * It means that the function should not return `61 seconds`, but `1 minute and 1 second` instead. 47 | * Formally, the duration specified by of a component must not be greater than any valid more significant unit of time. 48 | * 49 | * Tags: Strings, Date Time, Algorithms 50 | * 51 | */ 52 | 53 | enum TimeUnit { 54 | Years, 55 | Days, 56 | Hours, 57 | Minutes, 58 | Seconds 59 | } 60 | 61 | struct Time { 62 | TimeUnit unit; 63 | uint amount; 64 | 65 | /* 66 | * Converts struct for example to "3 seconds" 67 | */ 68 | string toString() const { 69 | return amount.to!string ~ ' ' ~ getUnitName(this); 70 | } 71 | } 72 | 73 | /* 74 | * Gives name of unit in plural or singular form depend 75 | */ 76 | string getUnitName(Time unit) { 77 | switch (unit.unit) { 78 | case TimeUnit.Years: 79 | return (unit.amount > 1) ? "years" : "year"; 80 | case TimeUnit.Days: 81 | return (unit.amount > 1) ? "days" : "day"; 82 | case TimeUnit.Hours: 83 | return (unit.amount > 1) ? "hours" : "hour"; 84 | case TimeUnit.Minutes: 85 | return (unit.amount > 1) ? "minutes" : "minute"; 86 | case TimeUnit.Seconds: 87 | return (unit.amount > 1) ? "seconds" : "second"; 88 | default: 89 | return "Why do you read this? :^)"; 90 | } 91 | } 92 | 93 | /* 94 | * Converts seconds to list of units 95 | */ 96 | Time[] calculate(uint seconds) { 97 | Time[] time = []; 98 | 99 | uint a = seconds / (365*24*60*60); 100 | if (a != 0) { 101 | time ~= Time(TimeUnit.Years, a); 102 | seconds %= 365*24*60*60; 103 | } 104 | a = seconds / (24*60*60); 105 | if (a != 0) { 106 | time ~= Time(TimeUnit.Days, a); 107 | seconds %= 24*60*60; 108 | } 109 | a = seconds / (60*60); 110 | if (a != 0) { 111 | time ~= Time(TimeUnit.Hours, a); 112 | seconds %= 60*60; 113 | } 114 | a = seconds / 60; 115 | if (a != 0) { 116 | time ~= Time(TimeUnit.Minutes, a); 117 | seconds %= 60; 118 | } 119 | if (seconds != 0) { 120 | time ~= Time(TimeUnit.Seconds, seconds); 121 | } 122 | return time; 123 | 124 | } 125 | 126 | string formatDuration(uint seconds) { 127 | if (seconds == 0) return "now"; 128 | 129 | Time[] time = calculate(seconds); 130 | 131 | if (time.length == 1) { 132 | return time[0].to!string; 133 | } 134 | 135 | string result; 136 | for (int i = 0; i < time.length; i++) { 137 | result ~= time[i].to!string; 138 | if (i == time.length - 1) break; 139 | if (i == time.length - 2) result ~= " and "; 140 | else result ~= ", "; 141 | } 142 | return result; 143 | } 144 | 145 | unittest { 146 | assert(formatDuration(0) == "now"); 147 | assert(formatDuration(1) == "1 second"); 148 | assert(formatDuration(62) == "1 minute and 2 seconds"); 149 | assert(formatDuration(120) == "2 minutes"); 150 | assert(formatDuration(3600) == "1 hour"); 151 | assert(formatDuration(3662) == "1 hour, 1 minute and 2 seconds"); 152 | } 153 | 154 | void main() {} -------------------------------------------------------------------------------- /src/data_structures/binary_heap.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | import std.algorithm : swap; 3 | 4 | /** 5 | * Implementation of a Min Heap (Minimum Priority Queue) data structure. 6 | * A Min Heap is a complete binary tree where the value of each node is 7 | * less than or equal to the values of its children. 8 | */ 9 | class MinHeap { 10 | private int[] harr; // Array to store heap elements 11 | private int capacity; // Maximum capacity of the heap 12 | private int heap_size; // Current number of elements in the heap 13 | 14 | /** 15 | * Constructs a new MinHeap with the specified capacity. 16 | * 17 | * @param cap the maximum number of elements the heap can hold 18 | */ 19 | this(int cap) { 20 | heap_size = 0; 21 | capacity = cap; 22 | harr = new int[cap]; 23 | } 24 | 25 | /** 26 | * Recursively heapifies a subtree rooted at index i to maintain the min-heap property. 27 | * 28 | * @param i the index of the root node of the subtree to heapify 29 | */ 30 | void minHeapify(int i) { 31 | int l = left(i); // Index of left child 32 | int r = right(i); // Index of right child 33 | int smallest = i; // Assume current node is the smallest 34 | 35 | // Compare with left child 36 | if (l < heap_size && harr[l] < harr[i]) 37 | smallest = l; 38 | // Compare with right child 39 | if (r < heap_size && harr[r] < harr[smallest]) 40 | smallest = r; 41 | // If smallest is not the current node, swap and continue heapifying 42 | if (smallest != i) { 43 | swap(harr[i], harr[smallest]); 44 | minHeapify(smallest); 45 | } 46 | } 47 | 48 | /** 49 | * Returns the index of the parent node for a given index. 50 | * 51 | * @param i the index of the child node 52 | * @return the index of the parent node 53 | */ 54 | int parent(int i) { return (i - 1) / 2; } 55 | 56 | /** 57 | * Returns the index of the left child for a given index. 58 | * 59 | * @param i the index of the parent node 60 | * @return the index of the left child node 61 | */ 62 | int left(int i) { return (2 * i + 1); } 63 | 64 | /** 65 | * Returns the index of the right child for a given index. 66 | * 67 | * @param i the index of the parent node 68 | * @return the index of the right child node 69 | */ 70 | int right(int i) { return (2 * i + 2); } 71 | 72 | /** 73 | * Removes and returns the minimum element (root) from the heap. 74 | * Maintains the heap property after removal. 75 | * 76 | * @return the minimum element in the heap, or int.max if heap is empty 77 | */ 78 | int extractMin() { 79 | if (heap_size <= 0) 80 | return int.max; 81 | if (heap_size == 1) { 82 | heap_size--; 83 | return harr[0]; 84 | } 85 | 86 | int root = harr[0]; // Store the root value 87 | harr[0] = harr[heap_size - 1]; // Move last element to root 88 | heap_size--; 89 | minHeapify(0); // Heapify from the root 90 | 91 | return root; 92 | } 93 | 94 | /** 95 | * Decreases the value of a key at the specified index to a new value. 96 | * Adjusts the heap to maintain the min-heap property. 97 | * 98 | * @param i the index of the key to decrease 99 | * @param new_val the new value for the key (must be smaller than current value) 100 | */ 101 | void decreaseKey(int i, int new_val) { 102 | harr[i] = new_val; 103 | // Bubble up the decreased key to maintain heap property 104 | while (i != 0 && harr[parent(i)] > harr[i]) { 105 | swap(harr[i], harr[parent(i)]); 106 | i = parent(i); 107 | } 108 | } 109 | 110 | /** 111 | * Returns the minimum element from the heap without removing it. 112 | * 113 | * @return the minimum element in the heap (root element) 114 | */ 115 | int getMin() { return harr[0]; } 116 | 117 | /** 118 | * Deletes a key at the specified index from the heap. 119 | * 120 | * @param i the index of the key to delete 121 | */ 122 | void deleteKey(int i) { 123 | decreaseKey(i, int.min); // Decrease key to minimum value 124 | extractMin(); // Extract the now-minimum key 125 | } 126 | 127 | /** 128 | * Inserts a new key into the heap. 129 | * Maintains the heap property after insertion. 130 | * 131 | * @param k the key value to insert 132 | */ 133 | void insertKey(int k) { 134 | if (heap_size == capacity) { 135 | writeln("\nOverflow: Could not insertKey"); 136 | return; 137 | } 138 | 139 | heap_size++; 140 | int i = heap_size - 1; 141 | harr[i] = k; 142 | 143 | // Bubble up the new key to maintain heap property 144 | while (i != 0 && harr[parent(i)] > harr[i]) { 145 | swap(harr[i], harr[parent(i)]); 146 | i = parent(i); 147 | } 148 | } 149 | } 150 | 151 | /** 152 | TESTS 153 | */ 154 | unittest { 155 | // Test basic insertion and extraction in order 156 | auto h = new MinHeap(11); 157 | h.insertKey(3); 158 | h.insertKey(2); 159 | h.insertKey(1); 160 | assert(h.extractMin() == 1); 161 | assert(h.extractMin() == 2); 162 | assert(h.extractMin() == 3); 163 | assert(h.extractMin() == int.max); // Test empty heap extraction 164 | 165 | } 166 | 167 | /** 168 | * Program tests are performed above in the unittest block. 169 | * The main function is empty as tests are run automatically. 170 | */ 171 | void main() {} 172 | -------------------------------------------------------------------------------- /src/math/math_parser.d: -------------------------------------------------------------------------------- 1 | import std; 2 | 3 | /** 4 | * The Parser class is responsible for parsing and evaluating mathematical expressions. 5 | */ 6 | class Parser { 7 | private string input; // The input string containing the expression to parse. 8 | private int pos; // The current position in the input string. 9 | 10 | /** 11 | * Constructs a Parser with the given input string. 12 | * 13 | * @param input The string representation of the mathematical expression. 14 | */ 15 | this(string input) { 16 | this.input = input; 17 | this.pos = 0; // Initialize position to the start of the input. 18 | } 19 | 20 | /** 21 | * Peeks at the next character in the input without consuming it. 22 | * 23 | * @return The next character or '0' if the end of the input is reached. 24 | */ 25 | private char peek() { 26 | if (pos < input.length) { 27 | return input[pos]; // Return the current character at position pos. 28 | } 29 | return '0'; // Return '0' if we are at the end of the input. 30 | } 31 | 32 | /** 33 | * Consumes and returns the next character in the input. 34 | * 35 | * @return The next character or '0' if the end of the input is reached. 36 | */ 37 | private char next() { 38 | if (pos < input.length) { 39 | return input[pos++]; // Return the current character and increment position. 40 | } 41 | return '0'; // Return '0' if we are at the end of the input. 42 | } 43 | 44 | /** 45 | * Skips whitespace characters in the input. 46 | */ 47 | private void skipWhitespace() { 48 | // Continue skipping while there are characters left and they are whitespace. 49 | while (pos < input.length && (peek == ' ' || peek == 't')) { 50 | next(); // Consume whitespace characters. 51 | } 52 | } 53 | 54 | /** 55 | * Parses an expression, which may consist of terms combined by '+' or '-'. 56 | * 57 | * @return The result of evaluating the expression as a double. 58 | */ 59 | double parseExpr() { 60 | double result = parseTerm(); // Start with the first term. 61 | while (true) { 62 | skipWhitespace(); // Skip any whitespace before the operator. 63 | char op = peek(); // Peek at the next character (operator). 64 | if (op == '+' || op == '-') { // Check for addition or subtraction. 65 | next(); // Consume the operator. 66 | double rhs = parseTerm(); // Parse the right-hand side term. 67 | if (op == '+') { 68 | result += rhs; // Add to result if operator is '+'. 69 | } else { 70 | result -= rhs; // Subtract from result if operator is '-'. 71 | } 72 | } else { 73 | break; // Exit loop if no more '+' or '-' operators are found. 74 | } 75 | } 76 | return result; // Return the final evaluated result. 77 | } 78 | 79 | /** 80 | * Parses a term, which may consist of factors combined by '*' or '/'. 81 | * 82 | * @return The result of evaluating the term as a double. 83 | */ 84 | double parseTerm() { 85 | double result = parseFactor(); // Start with the first factor. 86 | while (true) { 87 | skipWhitespace(); // Skip any whitespace before the operator. 88 | char op = peek(); // Peek at the next character (operator). 89 | if (op == '*' || op == '/') { // Check for multiplication or division. 90 | next(); // Consume the operator. 91 | double rhs = parseFactor(); // Parse the right-hand side factor. 92 | if (op == '*') { 93 | result *= rhs; // Multiply result if operator is '*'. 94 | } else { 95 | result /= rhs; // Divide result if operator is '/'. 96 | } 97 | } else { 98 | break; // Exit loop if no more '*' or '/' operators are found. 99 | } 100 | } 101 | return result; // Return the final evaluated term result. 102 | } 103 | 104 | /** 105 | * Parses a factor, which may be a number or a nested expression in parentheses. 106 | * 107 | * @return The result of evaluating the factor as a double. 108 | */ 109 | double parseFactor() { 110 | skipWhitespace(); // Skip any whitespace before processing factor. 111 | if (peek() == '(') { // Check for opening parenthesis indicating a nested expression. 112 | next(); // Consume the '(' character. 113 | double result = parseExpr(); // Parse the nested expression. 114 | next(); // Consume the closing ')' character. 115 | return result; // Return the evaluated result of the nested expression. 116 | } else { 117 | return parseNumber(); // Otherwise, parse a number directly. 118 | } 119 | } 120 | 121 | /** 122 | * Parses a number from the input string. 123 | * 124 | * @return The parsed number as a double. 125 | */ 126 | double parseNumber() { 127 | skipWhitespace(); // Skip any whitespace before processing number. 128 | size_t start = pos; // Mark the start position of the number. 129 | while (pos < input.length && (isDigit(peek()) || peek == '.')) { 130 | next(); // Consume digits or decimal point to form a number. 131 | } 132 | return to!double(input[start..pos]); // Convert and return the substring as a double. 133 | } 134 | } 135 | 136 | /* TESTS */ 137 | double testExpr(string expr){ 138 | Parser parser = new Parser(expr); 139 | return parser.parseExpr(); 140 | } 141 | 142 | unittest{ 143 | 144 | assert(testExpr("2+4") == 6); 145 | assert(testExpr("3 + (67 - 17)") == 53); 146 | assert(testExpr("2 * 3 * 4 + (2 * 4)") == 32); 147 | } 148 | 149 | /*Program tests are performed above in the unittest block*/ 150 | int main() { 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /src/problems/codewars/screen_locking_patterns.d: -------------------------------------------------------------------------------- 1 | import std; 2 | import std.algorithm : filter, map; 3 | import std.array : array; 4 | import std.conv : to; 5 | 6 | /* 7 | * Link: https://www.codewars.com/kata/585894545a8a07255e0002f1 8 | * 9 | * Screen Locking Patterns 10 | * Difficulty: 3 kyu 11 | * 12 | * Problem: 13 | * 14 | * You might already be familiar with many smartphones that allow you to use a geometric pattern as a security measure. 15 | * To unlock the device, you need to connect a sequence of dots/points in a grid by swiping your finger without lifting it as you trace the pattern through the screen. 16 | * 17 | * The image below has an example pattern of 7 dots/points: (A -> B -> I -> E -> D -> G -> C). 18 | * 19 | * IMAGE (https://i.imgur.com/zmPNYdv.png) 20 | * 21 | * For this kata, your job is to implement a function that returns the number of possible patterns starting from a given first point, that have a given length. 22 | * 23 | * More specifically, for a function `count_patterns_from(first_point, length)`, the parameter `first_point` is a single-character string corresponding to the point in the grid (e.g.: `'A'`) where your patterns start, and the parameter `length` is an integer indicating the number of points (length) every pattern must have. 24 | * 25 | * For example, `countPatternsFrom("C", 2)`, should return the number of patterns starting from `'C'` that have `2` two points. 26 | * The return value in this case would be `5`, because there are 5 possible patterns: 27 | * 28 | * (C -> B), (C -> D), (C -> E), (C -> F) and (C -> H). 29 | * 30 | * Bear in mind that this kata requires returning the number of patterns, not the patterns themselves, so you only need to count them. 31 | * Also, the name of the function might be different depending on the programming language used, but the idea remains the same. 32 | * 33 | * Rules 34 | * 1) In a pattern, the dots/points cannot be repeated: they can only be used once, at most. 35 | * 2) In a pattern, any two subsequent dots/points can only be connected with direct straight lines in either of these ways: 36 | * 3) Horizontally: like (A -> B) in the example pattern image. 37 | * 4) Vertically: like (D -> G) in the example pattern image. 38 | * 5) Diagonally: like (I -> E), as well as (B -> I), in the example pattern image. 39 | * 6) Passing over a point between them that has already been 'used': like (G -> C) passing over E, in the example pattern image. 40 | * This is the trickiest rule. 41 | * Normally, you wouldn't be able to connect G to C, because E is between them, however when E has already been used as part the pattern you are tracing, you can connect G to C passing over E, because E is ignored, as it was already used once. 42 | * 43 | * The sample tests have some examples of the number of combinations for some cases to help you check your code. 44 | * 45 | * Fun fact: 46 | * 47 | * In case you're wondering out of curiosity, for the Android lock screen, the valid patterns must have between 4 and 9 dots/points. 48 | * There are 389112 possible valid patterns in total; that is, patterns with a length between 4 and 9 dots/points. 49 | * 50 | * Tags: Mathematics, Combinatorics, Geometry, Algorithms, Graph Theory 51 | * 52 | */ 53 | 54 | char[] get_next_possible_dots(char dot, char[] used) { 55 | char[] possible; 56 | switch (dot) { 57 | case 'A': 58 | possible = ['B', 'D', 'E', 'F', 'H']; 59 | break; 60 | case 'B': 61 | possible = ['A', 'C', 'D', 'E', 'F', 'G', 'I']; 62 | break; 63 | case 'C': 64 | possible = ['B', 'D', 'E', 'F', 'H']; 65 | break; 66 | case 'D': 67 | possible = ['A', 'B', 'C', 'E', 'G', 'H', 'I']; 68 | break; 69 | case 'E': 70 | possible = ['A', 'B', 'C', 'D', 'F', 'G', 'H', 'I']; 71 | break; 72 | case 'F': 73 | possible = ['A', 'B', 'C', 'E', 'G', 'H', 'I']; 74 | break; 75 | case 'G': 76 | possible = ['B', 'D', 'E', 'F', 'H']; 77 | break; 78 | case 'H': 79 | possible = ['A', 'C', 'D', 'E', 'F', 'G', 'I']; 80 | break; 81 | case 'I': 82 | possible = ['B', 'D', 'E', 'F', 'H']; 83 | break; 84 | default: return []; 85 | } 86 | 87 | switch (dot) { 88 | case 'A': 89 | if (used.canFind('B')) possible ~= 'C'; 90 | if (used.canFind('D')) possible ~= 'G'; 91 | if (used.canFind('E')) possible ~= 'I'; 92 | break; 93 | case 'C': 94 | if (used.canFind('B')) possible ~= 'A'; 95 | if (used.canFind('E')) possible ~= 'G'; 96 | if (used.canFind('F')) possible ~= 'I'; 97 | break; 98 | case 'G': 99 | if (used.canFind('D')) possible ~= 'A'; 100 | if (used.canFind('E')) possible ~= 'C'; 101 | if (used.canFind('H')) possible ~= 'I'; 102 | break; 103 | case 'I': 104 | if (used.canFind('E')) possible ~= 'A'; 105 | if (used.canFind('F')) possible ~= 'C'; 106 | if (used.canFind('H')) possible ~= 'G'; 107 | break; 108 | case 'B': 109 | if (used.canFind('E')) possible ~= 'H'; 110 | break; 111 | case 'D': 112 | if (used.canFind('E')) possible ~= 'F'; 113 | break; 114 | case 'F': 115 | if (used.canFind('E')) possible ~= 'D'; 116 | break; 117 | case 'H': 118 | if (used.canFind('E')) possible ~= 'B'; 119 | break; 120 | default: break; 121 | } 122 | return possible.filter!(x => !used.canFind(x)).map!(c => to!char(c)).array; 123 | } 124 | 125 | uint count_patterns(char current, uint remaining_length, char[] used) { 126 | if (remaining_length == 1) return 1; 127 | 128 | uint count = 0; 129 | 130 | char[] next_dots = get_next_possible_dots(current, used); 131 | 132 | foreach (next_dot; next_dots) { 133 | count += count_patterns(next_dot, remaining_length - 1, used ~ next_dot); 134 | } 135 | 136 | return count; 137 | } 138 | 139 | uint count_patterns_from(char first_point, uint length) { 140 | if (length > 9 || length == 0) return 0; 141 | if (length == 1) return 1; 142 | 143 | return count_patterns(first_point, length, [first_point]); 144 | } 145 | 146 | unittest { 147 | assert(count_patterns_from('A', 0) == 0); 148 | assert(count_patterns_from('A', 10) == 0); 149 | assert(count_patterns_from('B', 1) == 1); 150 | assert(count_patterns_from('C', 2) == 5); 151 | assert(count_patterns_from('D', 3) == 37); 152 | assert(count_patterns_from('E', 4) == 256); 153 | assert(count_patterns_from('E', 8) == 23280); 154 | } 155 | 156 | void main() {} --------------------------------------------------------------------------------