├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md └── src ├── 100_doors.rs ├── 99_bottles_of_beer.rs ├── archimedean_spiral.rs ├── atbash_cipher.rs ├── binary_search.rs ├── bubble_sort.rs ├── caesar_cipher.rs ├── circle_sort.rs ├── cocktail_shaker_sort.rs ├── counting_sort.rs ├── factors_of_an_integer.rs ├── gnome_sort.rs ├── insertion_sort.rs ├── julia_set.rs ├── lib.rs ├── linear_search.rs ├── mandelbrot_set.rs ├── pancake_sort.rs ├── pascals_triangle.rs ├── password_generator.rs ├── pow_blockchain ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md └── src │ ├── blockchain.rs │ └── main.rs ├── quick_sort.rs ├── selection_sort.rs ├── shell_sort.rs ├── sleep_sort.rs └── two_thousand.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "data_structures_and_algorithms" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data_structures_and_algorithms" 3 | version = "0.1.0" 4 | 5 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data Structures and Algorithms in Rust 2 | 3 | ### What are Data Structures and Algorithms? 4 | 5 | A data structure is a specific type of data organization, management, and storage format that is usually 6 | optimized for accessing data efficiently. Essentially, it is a way of storing data that can be accessed, retrieved, 7 | and updated as fast as possible. 8 | 9 | There are two types of data structures: linear and non-linear. A linear data structure is one in which its elements 10 | are arranged sequentially and each element is attached to its neighbour. Non-linear data structures, however, are not 11 | ordered sequentially and their elements do not have to have any direct relation to their neighbours. 12 | 13 | Examples of linear data structures include: arrays, queues, and linked lists. Examples of non-linear data structures 14 | include: trees, and graphs. 15 | 16 | An algorithm refers to a set of finite instructions typically used to solve a problem, perform a computation, or 17 | accomplish a specific task. A recipe is a common example as it consists of a set of specific instructions for 18 | preparing a certain meal. In computer science, algorithms are used as specifications for performing various 19 | calculations, data processing, and automated reasoning. 20 | 21 | Learning data structures and algorithms helps you understand how to solve common problems in an effective manner 22 | as well as how to evaluate the efficiency of an algorithm, ie. its runtime complexity. 23 | 24 | ### Time Complexity 25 | 26 | Time complexity refers to the amount of time it takes to run an algorithm. Big O Notation is used to give a rough 27 | idea of how long an algorithm will take to run based on its input size and the amount of steps it takes to complete 28 | the algorithm. We use Big O Notation to describe runtime complexity in terms of worst case scenario; it 29 | doesn't matter if the program loops ten or a million times if the rate of change is the same. 30 | 31 | I have added comments at the top of most files regarding the algorithm's runtime complexity using Big O Notation. For 32 | the ciphers, I've decided to include security considerations at the top in place of runtime complexity. 33 | 34 | ## List of Data Structures and Algorithms 35 | 36 | ### Search Algorithms 37 | * [Linear Search](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/linear_search.rs) 38 | * [Binary Search](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/binary_search.rs) 39 | 40 | ### Sorting Algorithms 41 | * [Bubble Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/bubble_sort.rs) 42 | * [Circle Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/circle_sort.rs) 43 | * [Cocktail Shaker Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/cocktail_shaker_sort.rs) 44 | * [Counting Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/counting_sort.rs) 45 | * [Gnome Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/gnome_sort.rs) 46 | * [Insertion Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/insertion_sort.rs) 47 | * [Pancake Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/pancake_sort.rs) 48 | * [Quick Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/quick_sort.rs) 49 | * [Selection Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/selection_sort.rs) 50 | * [Shell Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/shell_sort.rs) 51 | * [Sleep Sort](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/sleep_sort.rs) 52 | 53 | ### Mathematical Algorithms 54 | * [Archimedean Spiral](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/archimedean_spiral.rs) 55 | * [Factors of an Integer](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/factors_of_an_integer.rs) 56 | * [Julia Set](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/julia_set.rs) 57 | * [Mandelbrot Set](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/mandelbrot_set.rs) 58 | * [Pascal's Triangle](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/pascals_triangle.rs) 59 | 60 | ### Ciphers 61 | * [Atbash Cipher](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/atbash_cipher.rs) 62 | * [Caesar Cipher](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/caesar_cipher.rs) 63 | 64 | ### Miscellaneous 65 | * [99 Bottles of Beer](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/99_bottles_of_beer.rs) 66 | * [100 Doors](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/100_doors.rs) 67 | * [Count to Two Thousand](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/two_thousand.rs) 68 | * [Password Generator](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/blob/main/src/password_generator.rs) 69 | * [Proof of Work Blockchain](https://github.com/0xIchigo/Data-Structures-and-Algorithms-in-Rust/tree/main/src/pow_blockchain) -------------------------------------------------------------------------------- /src/100_doors.rs: -------------------------------------------------------------------------------- 1 | // The problem at hand is as follows: There are 100 doors in a row that are initially closed. 2 | // You make 100 passes by the doors. The first time through, you visit every door and toggle 3 | // the door (if the door is closed, open it; if it is open, close it). The second time you 4 | // only visit evern second door (Door #2, #4, #6...) and toggle it. The third time you only 5 | // visit every third door (Door #3, #6, $9...), etc. You repeat this process until you only 6 | // visit the 100th door 7 | 8 | 9 | fn main() { 10 | let mut door_open = [false; 100]; 11 | for pass in 1..101 { 12 | let mut door = pass; 13 | while door <= 100 { 14 | door_open[door - 1] = !door_open[door - 1]; 15 | door += pass; 16 | } 17 | } 18 | 19 | for (i, &is_open) in door_open.iter().enumerate() { 20 | println!("Door {} is {}", i + 1, if is_open { "open" } else { "closed" }); 21 | } 22 | } 23 | 24 | // Ultra optimized version: 25 | 26 | // fn main() { 27 | // for i in 1u32..11u32 { 28 | // println!("Door {} is open", i.pow(2)); 29 | // } 30 | //} -------------------------------------------------------------------------------- /src/99_bottles_of_beer.rs: -------------------------------------------------------------------------------- 1 | // 99 Bottles of Beer is a traditional reverse counting song in Canada and the United States. It's 2 | // usually sung on road trips, family outings, school trips, or Scout / Girl Guide 3 | // outings, due to its repetitive, easy to memorize format 4 | 5 | // The song is also popular in programming circles as Tim Robinson maintained a website with 6 | // the song coded in various programming languages. The idea was initiated by a post to a humor mailing 7 | // list, where someone sent the full lyrics in 1994. https://www.99-bottles-of-beer.net/ is a continuation 8 | // of Tim's early site featuring over 1500 different programming languages and variations 9 | 10 | trait Bottles { 11 | fn bottles_of_beer(&self) -> Self; 12 | fn on_the_wall(&self); 13 | } 14 | 15 | impl Bottles for u32 { 16 | fn bottles_of_beer(&self) -> u32 { 17 | match *self { 18 | 0 => print!("No bottles of beer"), 19 | 1 => print!("{} bottle of beer", self), 20 | _ => print!("{} bottles of beer", self) 21 | } 22 | *self 23 | } 24 | 25 | fn on_the_wall(&self) { 26 | println!(" on the wall!"); 27 | } 28 | } 29 | 30 | fn main () { 31 | for i in (1..100).rev() { 32 | i.bottles_of_beer().on_the_wall(); 33 | i.bottles_of_beer(); 34 | println!("\nTake one down, pass it around..."); 35 | (i - 1).bottles_of_beer().on_the_wall(); 36 | println!("-----------------------------------"); 37 | } 38 | } -------------------------------------------------------------------------------- /src/archimedean_spiral.rs: -------------------------------------------------------------------------------- 1 | // The Archimedean spiral is a spiral named after the 3rd-century BC Greek mathmetician Archimedes. It 2 | // is the locus corresponding to the locations over time of a point moving away from a fixed point with 3 | // a constant speed along a line that rotates with constant angular velocity. Equivalently, in polar 4 | // coordinares (r, θ) it can be described by the equation: r = a + b ⋅ θ 5 | 6 | #[macro_use(px)] 7 | extern crate bmp; 8 | 9 | use bmp::{ Image, Pixel }; 10 | use std::f64; 11 | 12 | fn main() { 13 | let width = 600u32; 14 | let half_width = (width / 2) as i32; 15 | let mut img = Image::new(width, width); 16 | let draw_color = px!(255, 128, 128); 17 | 18 | let a = 1.0_f64; 19 | let b = 9.0_f64; 20 | let max_angle = 5.0_f64 * 2.0_f64 * f64::consts::PI; 21 | let mut theta = 0.0_f64; 22 | 23 | while theta < max_angle { 24 | theta += 0.002_f64; 25 | 26 | let r = a + b * theta; 27 | let x = (r * theta.cos()) as i32 + half_width; 28 | let y = (r * theta.sin()) as i32 + half_width; 29 | img.set_pixel(x as u32, y as u32, draw_color); 30 | } 31 | 32 | let _ = img.save("archimedean_spiral.bmp").unwrap_or_else(|e| panic!("Failed to save: {}", e)); 33 | } -------------------------------------------------------------------------------- /src/atbash_cipher.rs: -------------------------------------------------------------------------------- 1 | // Atbash is a monoalphabetic substitution cipher originally used to encrypt the Hebrew 2 | // alphabet. It is a very simple cipher wherein the alphabet used is mapped to its reverse 3 | // so the first letter becomes the last, the second letter becomes the second last letter, 4 | // and so forth. 5 | 6 | // Plain: abcdefghijklmnopqrstuvwxyz 7 | // Atbash Cipher: zyxwvutsrqponmlkjihgfedcba 8 | 9 | // Since there is only one way to perform this, the Atbash cipher provides no communications 10 | // security due to the lack of the key. If multiple collating orders are available and one was 11 | // encrypted as a key, this would provide only marginal security improvements as only a few 12 | // letters can give away which one was used. 13 | 14 | // Traditionally, the ciphertext is written out in groups of fixed length (5 letters) with 15 | // puncuation excluded making it harder to guess things based on word boundaries. This is why 16 | // we call .chunks(5) within atbash() 17 | 18 | use std::collections::HashMap; 19 | 20 | const FORWARDS: &'static str = "abcdefghijklmnopqrstuvwxyz"; 21 | const BACKWARDS: &'static str = "zyxwvutsrqponmlkjihgfedcba"; 22 | 23 | fn atbash(text: &str) -> Vec { 24 | // Mapping the substitution using .zip() 25 | let substitution: HashMap = FORWARDS.chars().zip(BACKWARDS.chars()).collect(); 26 | 27 | text.to_lowercase() 28 | .chars() 29 | .filter(|character| character.is_digit(36)) 30 | .collect::>() 31 | .chunks(5) 32 | .map(|chunk| chunk.iter().map(|&k| if substitution.contains_key(&k) { substitution.get(&k).unwrap().clone() } else { k })) 33 | .map(|chunk| chunk.collect::()) 34 | .collect::>() 35 | } 36 | 37 | fn transcode(text: &str) -> String { 38 | atbash(text).join("") 39 | } 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | 45 | #[test] 46 | fn test_atbash() { 47 | 48 | let mut test1 = "test"; 49 | assert_eq!(transcode(&mut test1), "gvhg"); 50 | 51 | let mut test2 = "gvhg"; 52 | assert_eq!(transcode(&mut test2), "test"); 53 | 54 | let mut test3 = "Hello there"; 55 | assert_eq!(transcode(&mut test3), "svoolgsviv"); 56 | 57 | let mut test4 = "General Kenobi"; 58 | assert_eq!(transcode(&mut test4), "tvmvizopvmlyr"); 59 | 60 | let mut test5 = "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt"; 61 | assert_eq!(transcode(&mut test5), "thequickbrownfoxjumpsoverthelazydog"); 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /src/binary_search.rs: -------------------------------------------------------------------------------- 1 | // A binary search (half-interval search/logarithmic search/binary chop) is a search algorithm that 2 | // finds a target value within a sorted array by comparing the target value to the middle of the array. 3 | // If they are not equal, the remaining half in which the target value cannot lie is eliminated, and the 4 | // search continues for each remaining half until the target value is found 5 | 6 | // In terms of runtime complexity, binary search runs in logarithmic time making O(log n) comparisons 7 | // where n is the number of elements in the array 8 | 9 | pub fn binary_search(item: &T, array: &[T]) -> i32 { 10 | let mut index = -1; 11 | 12 | if array.is_empty() { 13 | return index; 14 | } 15 | 16 | let mut left = 0; 17 | let mut right = array.len() - 1; 18 | 19 | while left < right { 20 | let mid = left + (right - left) / 2; 21 | 22 | if &array[mid] > item { 23 | right = mid - 1; 24 | } else if &array[mid] < item { 25 | left = mid + 1; 26 | } else { 27 | left = mid; 28 | break; 29 | } 30 | } 31 | 32 | if &array[left] == item { 33 | index = left as i32; 34 | return index; 35 | } else { 36 | return index; 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use super::*; 43 | 44 | #[test] 45 | fn test_binary_search() { 46 | assert_eq!(binary_search(&12, &vec![0, 3, 4, 12, 777]), 3); 47 | assert_eq!(binary_search(&100, &vec![0, 3, 4, 12, 777]), -1); 48 | assert_eq!(binary_search(&"Apple", &vec!["Apple", "Banana", "Grape", "Orange", "Pineapple"]), 0); 49 | } 50 | } -------------------------------------------------------------------------------- /src/bubble_sort.rs: -------------------------------------------------------------------------------- 1 | // Bubble sort is a type of sorting algorithm that repeatedly loops through elements of a list, comparing 2 | // the current element with the one after it, and swapping their values if necessary 3 | 4 | // In terms of runtime complexity, a bubble sort makes at most O(n^2) comparisons and O(n^2) swaps where 5 | // n is the length of the list 6 | 7 | pub fn bubble_sort(array: &mut [T]) -> Vec { 8 | if array.is_empty() { 9 | return vec![]; 10 | } 11 | 12 | for i in 0..array.len() { 13 | for j in 0..array.len() - 1 - i { 14 | if array[j] > array[j + 1] { 15 | array.swap(j, j + 1); 16 | } 17 | } 18 | } 19 | 20 | array.to_vec() 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn test_bubble_sort() { 29 | 30 | let mut test1 = vec![12, 39, 4, 36, 777]; 31 | assert_eq!(bubble_sort(&mut test1), vec![4, 12, 36, 39, 777]); 32 | 33 | let mut test2 = vec![21, 55, 14, -123, 32, 0]; 34 | assert_eq!(bubble_sort(&mut test2), vec![-123, 0, 14, 21, 32, 55]); 35 | 36 | let mut test3 = vec!["Orange", "Pear", "Apple", "Grape", "Banana"]; 37 | assert_eq!(bubble_sort(&mut test3), vec!["Apple", "Banana", "Grape", "Orange", "Pear"]); 38 | } 39 | } -------------------------------------------------------------------------------- /src/caesar_cipher.rs: -------------------------------------------------------------------------------- 1 | // The Caesar Cipher, also known as Caesar's code or simply the shift cipher, is one of the earliets and simplest methods 2 | // of encryption. It is a type of substitution cipher wherein each letter in plaintext is replaced by a letter some fixed 3 | // number of positions down the alphabet. The Caesar Cipher is named after Julius Caesar, who, according to Suetonis, used 4 | // it with a shift of three (A becomes D when encrypting, and D becomes I when decrypting) to protect messages of military 5 | // significance 6 | 7 | // Plain: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 8 | // Caesar Cipher: D E F G H I J K L M N O P Q R S T U V W X Y Z A B C 9 | 10 | // The Caesar Cipher can be broken even in a ciphertext-only scenario since there are only a limited number of possible shifts 11 | // (25 in English). An attacker can mount a brute force attack by deciphering the message, or part of it, using each possible 12 | // shift. It can also be easily cracked when graphing the frequency distribution of the letters in cipher text and by knowing 13 | // the expected distribution of those letters in the original language of the plaintext. 14 | 15 | // Encrypting a text multiple times with the Caesar Cipher provides no additional security because, say, two encryptions shift A 16 | // and shift B will be equivalent to a single encryption of A + B. The set of encryption operations under each possible key forms 17 | // a group under composition 18 | 19 | fn caesar(message: &str, shift: u8) -> String { 20 | message 21 | .chars() 22 | .map(|c| { 23 | if c.is_ascii_alphabetic() { 24 | let first_char = if c.is_ascii_lowercase() { b'a' } else { b'A' }; 25 | (first_char + (c as u8 + shift - first_char) % 26) as char 26 | } else { 27 | c 28 | } 29 | }) 30 | .collect() 31 | } 32 | 33 | #[cfg(test)] 34 | mod tests { 35 | use super::*; 36 | 37 | #[test] 38 | fn test_caesar() { 39 | 40 | let mut test1 = ""; 41 | assert_eq!(caesar(&mut test1, 10), ""); 42 | 43 | let mut test2 = "ATTACKATONCE"; 44 | assert_eq!(caesar(&mut test2, 4), "EXXEGOEXSRGI"); 45 | 46 | let mut test3 = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"; 47 | assert_eq!(caesar(&mut test3, 23), "QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD"); 48 | 49 | let mut test4 = "a"; 50 | assert_eq!(caesar(&mut test4, 3), "d"); 51 | 52 | let mut test5 = "general kenobi"; 53 | assert_eq!(caesar(&mut test5, 23), "dbkboxi hbklyf"); 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/circle_sort.rs: -------------------------------------------------------------------------------- 1 | // Circle sort is a sorting algorithm in which concentric circles are made on an array and the elements 2 | // lying on the same circle diametrically opposite to each other are compared, and if the element on the 3 | // left hand side is greater than that on the right hand side then the elements are swapped. This process 4 | // is repeated in a recursive manner, dividing the array into sub-arrays, until the array is sorted 5 | 6 | // Circle sort is an unstable, recursive, parallelizable, in place sorting algorithm. It is one of the 7 | // fastest ways to sort an inverted array with an average time complexity of O(n log(n)) and space 8 | // complexity of O(1). Description from: https://iq.opengenus.org/circle-sort/ 9 | 10 | fn _circle_sort(array: &mut[T], low: usize, high: usize, swaps: usize) -> usize { 11 | if low == high { 12 | return swaps; 13 | } 14 | 15 | let mut lo = low; 16 | let mut hi = high; 17 | let mid = (hi - low) / 2; 18 | let mut s = swaps; 19 | 20 | while lo < hi { 21 | if array[lo] > array[hi] { 22 | array.swap(lo, hi); 23 | s += 1; 24 | } 25 | 26 | lo += 1; 27 | hi -= 1; 28 | } 29 | 30 | if lo == hi { 31 | if array[lo] > array[hi + 1] { 32 | array.swap(lo, hi + 1); 33 | s += 1; 34 | } 35 | } 36 | 37 | s = _circle_sort(array, low, low + mid, s); 38 | s = _circle_sort(array, low + mid + 1, high, s); 39 | 40 | return s; 41 | } 42 | 43 | fn circle_sort(array: &mut[T]) -> Vec { 44 | let len = array.len(); 45 | 46 | if array.is_empty() { 47 | return vec![]; 48 | } 49 | 50 | loop { 51 | if _circle_sort(array, 0, len - 1, 0) == 0 { 52 | break; 53 | } 54 | } 55 | 56 | array.to_vec() 57 | } 58 | 59 | #[cfg(test)] 60 | mod tests { 61 | use super::*; 62 | 63 | #[test] 64 | fn test_circle_sort() { 65 | 66 | let mut test1 = vec![12, 39, 4, 36, 777]; 67 | assert_eq!(circle_sort(&mut test1), vec![4, 12, 36, 39, 777]); 68 | 69 | let mut test2 = vec![21, 55, 14, 123, 32, 0]; 70 | assert_eq!(circle_sort(&mut test2), vec![0, 14, 21, 32, 55, 123]); 71 | 72 | } 73 | } -------------------------------------------------------------------------------- /src/cocktail_shaker_sort.rs: -------------------------------------------------------------------------------- 1 | // The cocktail shaker sort, also known as bidirectional bubble sort, is an extension of 2 | // bubble sort such that it operates in two directions. While it improves on bubble sort by 3 | // moving items to the beginning of the list more quickly, it provides only marginal 4 | // performance improvements which is why it is used primarily as an education tool 5 | 6 | // In terms of runtime complexity, a cocktail shaker sort makes at most O(n^2) comparisons 7 | // and O(n^2) swaps where n is the length of the list 8 | 9 | fn cocktail_shaker_sort(array: &mut [T]) -> Vec { 10 | if array.is_empty() { 11 | return vec![]; 12 | } 13 | 14 | let length = array.len(); 15 | 16 | loop { 17 | let mut swapped = false; 18 | let mut i = 0; 19 | 20 | while i + 1 < length { 21 | if array[i] > array[i + 1] { 22 | array.swap(i, i + 1); 23 | swapped = true; 24 | } 25 | 26 | i += 1; 27 | } 28 | 29 | if swapped { 30 | swapped = false; 31 | i = length - 1; 32 | 33 | while i > 0 { 34 | if array[i - 1] > array[i] { 35 | array.swap(i, i - 1); 36 | swapped = true; 37 | } 38 | 39 | i -= 1; 40 | } 41 | } 42 | 43 | if !swapped { 44 | break; 45 | } 46 | } 47 | 48 | array.to_vec() 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use super::*; 54 | 55 | #[test] 56 | fn test_cocktail_shaker_sort() { 57 | 58 | let mut test1 = vec![12, 39, 4, 36, 777]; 59 | assert_eq!(cocktail_shaker_sort(&mut test1), vec![4, 12, 36, 39, 777]); 60 | 61 | let mut test2 = vec![21, 55, 14, -123, 32, 0]; 62 | assert_eq!(cocktail_shaker_sort(&mut test2), vec![-123, 0, 14, 21, 32, 55]); 63 | 64 | let mut test3 = vec!["Orange", "Pear", "Apple", "Grape", "Banana"]; 65 | assert_eq!(cocktail_shaker_sort(&mut test3), vec!["Apple", "Banana", "Grape", "Orange", "Pear"]); 66 | } 67 | } -------------------------------------------------------------------------------- /src/counting_sort.rs: -------------------------------------------------------------------------------- 1 | // Counting sort is a sorting algorithm for sorting a collection of keys between a specific 2 | // range. It operates by counting the number of objects having distinct key, and then applies 3 | // a prefix sum on those counts to determine the positions of each key value in the output 4 | // sequence. It is often used as a subroutine in a radix sort, another sorting algorithm which 5 | // can handle larger keys more efficiently. 6 | 7 | // In terms of runtime complexity, a counting sort makes at most O(n + k) calculations wherein k 8 | // is the range of the non-negative key values 9 | 10 | use std::ops::AddAssign; 11 | 12 | fn counting_sort + From + AddAssign + Copy>(array: &mut [T], max: usize) -> Vec { 13 | let mut count_array: Vec = vec![0; max + 1]; 14 | 15 | for &num in array.iter() { 16 | count_array[num.into() as usize] += 1; 17 | } 18 | 19 | let mut i: usize = 0; 20 | let mut data = T::from(0); 21 | 22 | for &number in count_array.iter() { 23 | for _ in 0..number { 24 | array[i] = data; 25 | i += 1; 26 | } 27 | 28 | data += T::from(1); 29 | } 30 | 31 | array.to_vec() 32 | } 33 | 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | 39 | #[test] 40 | fn test_counting_sort() { 41 | 42 | let mut test1 = vec![12, 39, 4, 36, 777]; 43 | assert_eq!(counting_sort(&mut test1, 777), vec![4, 12, 36, 39, 777]); 44 | 45 | let mut test2 = vec![21, 55, 14, 123, 32, 0]; 46 | assert_eq!(counting_sort(&mut test2, 123), vec![0, 14, 21, 32, 55, 123]); 47 | 48 | } 49 | } -------------------------------------------------------------------------------- /src/factors_of_an_integer.rs: -------------------------------------------------------------------------------- 1 | // This algorithm computes the factors of a positive integer. These factors are positive 2 | // integers by which the number being factored can be divided to yield a positive integer 3 | // result. Prime numbers will only return a vector of two factors: 1 and itself 4 | 5 | fn main() { 6 | assert_eq!(vec![1, 2, 7, 14], factor(14)); 7 | assert_eq!(vec![1, 3], factor(3)); 8 | assert_eq!(vec![1, 2, 4, 5, 10, 10, 20, 25, 50, 100], factor(100)); 9 | } 10 | 11 | fn factor(num: i32) -> Vec { 12 | let mut factors: Vec = Vec::new(); 13 | 14 | for i in 1..((num as f32).sqrt() as i32 + 1) { 15 | if num % i == 0 { 16 | factors.push(i); 17 | factors.push(num / i); 18 | } 19 | } 20 | 21 | factors.sort(); 22 | factors 23 | } 24 | 25 | fn alternative_factor_function(n: i32) -> Vec { 26 | (1..=n).filter(|i| n % i == 0).collect() 27 | } -------------------------------------------------------------------------------- /src/gnome_sort.rs: -------------------------------------------------------------------------------- 1 | // Gnome sort is a variation of the insertion sort algorithm that does not use nested loops. It was initially 2 | // proposed by Hamid Sarbazi-Azad in 2000 and was referred to as stupid sort, Dutch computer scientist Dick 3 | // Grune renamed it the gnome sort in light of a story describing how Dutch Garden Gnomes sort their flower pots 4 | 5 | // In terms of runtime complexity, a gnome sort makes at most O(n^2) comparisons and O(n^2) swaps where n is the 6 | // length of the list 7 | 8 | 9 | fn gnome_sort(array: &mut [T]) -> Vec { 10 | if array.is_empty() { 11 | return vec![]; 12 | } 13 | 14 | let length = array.len(); 15 | let mut i: usize = 1; 16 | let mut j: usize = 2; 17 | 18 | while i < length { 19 | if array[i - 1] < array[i] { 20 | i = j; 21 | j = i + 1; 22 | } else { 23 | array.swap(i - 1, i); 24 | i -= 1; 25 | 26 | if i == 0 { 27 | i = j; 28 | j += 1; 29 | } 30 | } 31 | } 32 | 33 | array.to_vec() 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | 40 | #[test] 41 | fn test_gnome_sort() { 42 | 43 | let mut test1 = vec![12, 39, 4, 36, 777]; 44 | assert_eq!(gnome_sort(&mut test1), vec![4, 12, 36, 39, 777]); 45 | 46 | let mut test2 = vec![21, 55, 14, -123, 32, 0]; 47 | assert_eq!(gnome_sort(&mut test2), vec![-123, 0, 14, 21, 32, 55]); 48 | 49 | let mut test3 = vec!["Orange", "Pear", "Apple", "Grape", "Banana"]; 50 | assert_eq!(gnome_sort(&mut test3), vec!["Apple", "Banana", "Grape", "Orange", "Pear"]); 51 | } 52 | } -------------------------------------------------------------------------------- /src/insertion_sort.rs: -------------------------------------------------------------------------------- 1 | // Insertion sort is a simple sorting algorithm that builds the final sorted list one element at a time. It 2 | // is much less efficient than more advanced algoriths, such as quick sort or merge sort, on larger lists. 3 | // Insertion sort does, however, bolsters several advantages given its simple implementation, effectiveness 4 | // on small data sets, its adaptive nature, and it is more efficient in practice than other simple quadratic 5 | // algorithms such as bubble or selection sort. 6 | 7 | // In terms of runtime complexity, an insertion sort makes at most O(n^2) comparisons and O(n^2) swaps where 8 | // n is the length of the list 9 | 10 | fn insertion_sort(array: &mut [T]) -> Vec { 11 | if array.len() == 0 { 12 | return vec![]; 13 | } 14 | 15 | let length = array.len(); 16 | 17 | for i in 1..length { 18 | let mut j = i - 1; 19 | let k = array[i].clone(); 20 | 21 | while array[j] > k { 22 | array[j + 1] = array[j].clone(); 23 | if j == 0 { 24 | break; 25 | } 26 | 27 | j -= 1; 28 | } 29 | 30 | if j == 0 && array[0] > k { 31 | array[0] = k; 32 | } else { 33 | array[j + 1] = k; 34 | } 35 | } 36 | 37 | array.to_vec() 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use super::*; 43 | 44 | #[test] 45 | fn test_insertion_sort() { 46 | 47 | let mut test1 = vec![12, 39, 4, 36, 777]; 48 | assert_eq!(insertion_sort(&mut test1), vec![4, 12, 36, 39, 777]); 49 | 50 | let mut test2 = vec![21, 55, 14, -123, 32, 0]; 51 | assert_eq!(insertion_sort(&mut test2), vec![-123, 0, 14, 21, 32, 55]); 52 | 53 | let mut test3 = vec!["Orange", "Pear", "Apple", "Grape", "Banana"]; 54 | assert_eq!(insertion_sort(&mut test3), vec!["Apple", "Banana", "Grape", "Orange", "Pear"]); 55 | } 56 | } -------------------------------------------------------------------------------- /src/julia_set.rs: -------------------------------------------------------------------------------- 1 | // A Julia set, named after French mathematician Gaston Julia, is a set of complex numbers 2 | // which do not converge to any limit when a given mapping is applied to them repeatedly. It 3 | // has a complimentary set - the Fatou set, named after French mathematician Pierre Fatou. 4 | // While less famous than the Mandelbrot set, the Julia set gives rise to breathtaking visuals 5 | // all while expressing a complex and rich set of dynamics first explored in the 20th century. 6 | 7 | extern crate image 8 | use image::{ ImageBuffer, Pixel, Rbg }; 9 | 10 | fn julia_set() { 11 | let width = 12000; 12 | let height = 9000; 13 | let mut img = ImageBuffer::new(width as u32, height as u32); 14 | 15 | // Constants to tweak the appearance 16 | let cx = -0.9; 17 | let cy = 0.27015; 18 | let iterations = 110; 19 | 20 | for x in 0..width { 21 | for y in 0..height { 22 | let inner_height = height as f32; 23 | let inner_width = width as f32; 24 | let inner_y = y as f32; 25 | let inner_x = x as f32; 26 | 27 | let mut zx = 3.0 * (inner_x - 0.5 * inner_width) / (inner_width); 28 | let mut zy = 2.0 * (inner_y - 0.5 * inner_height) / (inner_height); 29 | 30 | let mut i = iterations; 31 | 32 | while zx * zx + zy * zy < 4.0 && i > 1 { 33 | let tmp = zx * zx - zx * zy + cx; 34 | zy = 2.0 * zx * zy + cy; 35 | zx = tmp; 36 | i -= 1; 37 | } 38 | 39 | let r = (i << 3) as u8; 40 | let g = (i << 5) as u8; 41 | let b = (i << 4) as u8; 42 | let pixel = Rgb::from_channels(r, g, b, 0); 43 | img.put_pixel(x as u32, y as u32, pixel); 44 | } 45 | } 46 | 47 | let _ = img.save("output.png"); 48 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod linear_search; 2 | mod binary_search; 3 | 4 | mod bubble_sort; 5 | mod circle_sort; 6 | mod cocktail_shaker_sort; 7 | mod counting_sort; 8 | mod gnome_sort; 9 | mod insertion_sort; 10 | mod pancake_sort; 11 | mod quick_sort; 12 | mod selection_sort; 13 | mod shell_sort; 14 | 15 | mod pascals_triangle; 16 | 17 | mod atbash_cipher; 18 | mod caesar_cipher; -------------------------------------------------------------------------------- /src/linear_search.rs: -------------------------------------------------------------------------------- 1 | // A linear search is a method for finding an element within a list by checking each element of 2 | // the list sequentially until the desired element is found, or the entire list has been searched through. 3 | 4 | // In terms of runtime complexity, a linear search makes at most n comparisons where n is the length of the list 5 | 6 | pub fn linear_search(item: &T, array: &[T]) -> i32{ 7 | let mut index_position = -1; 8 | 9 | for (index, data) in array.iter().enumerate() { 10 | if item == data { 11 | index_position = index as i32; 12 | return index_position; 13 | } 14 | } 15 | 16 | index_position 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | 23 | #[test] 24 | fn test_linear_search() { 25 | assert_eq!(linear_search(&12, &vec![12, 3, 4, 36, 777]), 0); 26 | assert_eq!(linear_search(&100, &vec![12, 3, 4, 36, 777]), -1); 27 | assert_eq!(linear_search(&"Apple", &vec!["Orange", "Pear", "Apple", "Grape", "Banana"]), 2); 28 | } 29 | } -------------------------------------------------------------------------------- /src/mandelbrot_set.rs: -------------------------------------------------------------------------------- 1 | // Mandelbrot set is used to refer to both a general class of fractal sets and to a particular instance 2 | // of such a set. In general, a Mandelbrot set marks the set of points in the complex plane such that the 3 | // corresponding Julia set is connected and not computable. The set was first defined and drawn by Robert 4 | // W. Brooks and Peter Matelski in 1978, however in 1980 Benoit Mandelbrot obtained high quality visualizations 5 | // of the set while working at IBM's Thomas J. Watson Research Center. 6 | 7 | // The Mandelbrot set has become popular outside of mathematics both for its aesthetic appeal and as an 8 | // example of a complex structure arising from the application of simple rules. 9 | 10 | use num::complex::Complex; 11 | 12 | fn calculate_mandelbrot( 13 | max_iters: usize, 14 | x_min: f64, 15 | x_max: f64, 16 | y_min: f64, 17 | y_max: f64, 18 | width: usize, 19 | height: usize, 20 | ) -> Vec> { 21 | let mut rows: Vec<_> = Vec::with_capacity(width); 22 | 23 | for img_y in 0..height { 24 | let mut row: Vec = Vec::with_capacity(height); 25 | 26 | for img_x in 0..width { 27 | let x_percent = (img_x as f64 / width as f64); 28 | let y_percent = (img_y as f64 / height as f64); 29 | let cx = x_min + (x_max - x_min) * x_percent; 30 | let cy = y_min + (y_max - y_min) * y_percent; 31 | let escaped_at = mandelbrot_at_point(cx, cy, max_iters); 32 | row.push(escaped_at); 33 | } 34 | 35 | rows.push(row); 36 | } 37 | 38 | rows 39 | } 40 | 41 | fn mandelbrot_at_point( 42 | cx: f64, 43 | cy: f64, 44 | max_iters: usize, 45 | ) -> usize { 46 | // Initializing a complex number at the origin with real and imaginary parts at 0.0 47 | let mut z = Complex { re: 0.0, im: 0.0 }; 48 | let c = Complex::new(cx, cy); 49 | 50 | for i in 0..=max_iters { 51 | if z.norm() > 2.0 { 52 | return i; 53 | } 54 | 55 | z = z * z + c; 56 | } 57 | 58 | max_iters 59 | } 60 | 61 | fn render_mandelbrot(escape_vals: Vec>) { 62 | for row in escape_vals { 63 | let mut line = String::with_capacity(row.len()); 64 | for column in row { 65 | let val = match column { 66 | 0..=2 => " ", 67 | 2..=5 => ".", 68 | 5..=10 => "•", 69 | 11..=30 => "*", 70 | 30..=100 => "+", 71 | 100..=200 => "x", 72 | 200..=400 => "$", 73 | 400..=700 => "#", 74 | _ => "%", 75 | }; 76 | 77 | line.push(val); 78 | } 79 | 80 | println!("{}", line); 81 | } 82 | } 83 | 84 | fn main() { 85 | let mandelbrot = calculate_mandelbrot(1000, 2.0, 1.0, -1.0, 1.0, 100, 24); 86 | render_mandelbrot(mandelbrot); 87 | } -------------------------------------------------------------------------------- /src/pancake_sort.rs: -------------------------------------------------------------------------------- 1 | // The pancake sorting algorithm derives from the mathematical problem of sorting a 2 | // disorganized stack of pancakes in order of size when a spatula can be inserted at 3 | // any point in the stack and is used to flipp all pancakes above it. Pancake number 4 | // refers to the minimum number of flips required for a given number of pancakes. 5 | 6 | // In terms of runtime complexity, a pancake sort makes at most O(n^2) comparisons 7 | // where, worst case scenario, it would make two loops of n iterations 8 | 9 | fn pancake_sort(array: &mut [T]) -> Vec { 10 | if array.is_empty() { 11 | return vec![]; 12 | } 13 | 14 | let length = array.len(); 15 | 16 | if length < 2 { 17 | return array.to_vec(); 18 | } 19 | 20 | // Iterate in reverse order 21 | for i in (0..length).rev() { 22 | let max_index = array.iter() 23 | .take(i + 1) 24 | .enumerate() 25 | .max_by_key(|&(_, element) | element) 26 | .map(|(idx, _)| idx) 27 | .unwrap(); 28 | 29 | if max_index != i { 30 | flip(array, max_index); 31 | flip(array, i); 32 | } 33 | } 34 | 35 | array.to_vec() 36 | } 37 | 38 | fn flip(array: &mut [E], num: usize) { 39 | array[0..num + 1].reverse(); 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use super::*; 45 | 46 | #[test] 47 | fn test_pancake_sort() { 48 | 49 | let mut test1 = vec![12, 39, 4, 36, 777]; 50 | assert_eq!(pancake_sort(&mut test1), vec![4, 12, 36, 39, 777]); 51 | 52 | let mut test2 = vec![21, 55, 14, -123, 32, 0]; 53 | assert_eq!(pancake_sort(&mut test2), vec![-123, 0, 14, 21, 32, 55]); 54 | 55 | let mut test3 = vec!["Orange", "Pear", "Apple", "Grape", "Banana"]; 56 | assert_eq!(pancake_sort(&mut test3), vec!["Apple", "Banana", "Grape", "Orange", "Pear"]); 57 | } 58 | } -------------------------------------------------------------------------------- /src/pascals_triangle.rs: -------------------------------------------------------------------------------- 1 | // Pascal's triangle is a triangular array of the binomial coefficients that arises 2 | // in probability theory, combinatorics, and algebra named after the French mathematician 3 | // Blaise Pascal. The triangle begins with a one at the top and with ones running down 4 | // the two sides of the traingle. Each new number lies between two numbers and below them, 5 | // and its value is the sum of the two numbers above it 6 | 7 | // In terms of runtime complexity, a Pascal's triangle makes at most O(n^2) calculations 8 | 9 | fn pascals_triangle(rows: i32) -> Vec> { 10 | let mut triangle: Vec> = vec![]; 11 | 12 | for i in 1..rows + 1 { 13 | let mut vec: Vec = vec![1]; 14 | let mut j: i32 = 1; 15 | 16 | for k in 1..i { 17 | j *= i - k; 18 | j /= k; 19 | 20 | vec.push(j); 21 | } 22 | triangle.push(vec); 23 | } 24 | 25 | triangle 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn test_pascal_triangle() { 34 | assert_eq!(pascals_triangle(1), vec![vec![1]]); 35 | assert_eq!(pascals_triangle(2), vec![vec![1], vec![1, 1]]); 36 | assert_eq!(pascals_triangle(3), vec![vec![1], vec![1, 1], vec![1, 2, 1]]); 37 | assert_eq!(pascals_triangle(4), vec![vec![1], vec![1, 1], vec![1, 2, 1], vec![1, 3, 3, 1]]); 38 | } 39 | } -------------------------------------------------------------------------------- /src/password_generator.rs: -------------------------------------------------------------------------------- 1 | // A password generator is an algorithm that creates random or customized passwords 2 | // for users. It helps users create stronger passwords that provide greater security 3 | // in comparison to passwords one may come up with on their own 4 | 5 | use rand::distributions::Alphanumeric; 6 | use rand::prelude::IteratorRandom; 7 | use rand::{ thread_rng, Rng }; 8 | use std::iter; 9 | use std::process; 10 | 11 | const OTHER_VALUES: &str = "!\"#$%&'()*+,-./:;<=>?@[]^_{|}~"; 12 | 13 | fn generate_password(length: u8) -> String { 14 | let mut rng = thread_rng(); // Caching for better performance 15 | let mut base_password: Vec = iter::repeat(()).map(|()| rng.sample(Alphanumeric).take(length as usize)).collect(); 16 | let mut end_range = 10; 17 | 18 | if length < end_range { 19 | end_range = length; 20 | } 21 | 22 | let mut to_add = rng.gen_range(1, end_range as usize); 23 | 24 | loop { 25 | let special = OTHER_VALUES.chars().choose(&mut rng).unwrap(); 26 | to_add -= 1; 27 | base_password[to_add] = special; 28 | if to_add == 0 { break; } 29 | } 30 | 31 | base_password.iter().collect() 32 | } 33 | 34 | fn main() { 35 | let opt = Opt::from_args(); 36 | const MINIMUM_LENGTH = 30; 37 | 38 | if opt.length < MINIMUM_LENGTH { 39 | eprintln!("Please provide a password length greater than or equal to {}", MINIMUM_LENGTH); 40 | process::exit(1); 41 | } 42 | 43 | for index in 0..opt.count { 44 | let password = generate_password(length); 45 | match index + 1 == opt.count { 46 | true => print!("{}", password), 47 | _ => println!("{}", password), 48 | }; 49 | } 50 | } -------------------------------------------------------------------------------- /src/pow_blockchain/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /src/pow_blockchain/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arrayref" 7 | version = "0.3.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 10 | 11 | [[package]] 12 | name = "block-buffer" 13 | version = "0.3.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" 16 | dependencies = [ 17 | "arrayref", 18 | "byte-tools", 19 | ] 20 | 21 | [[package]] 22 | name = "blockchain" 23 | version = "0.1.0" 24 | dependencies = [ 25 | "serde", 26 | "serde_derive", 27 | "serde_json", 28 | "sha2", 29 | "time", 30 | ] 31 | 32 | [[package]] 33 | name = "byte-tools" 34 | version = "0.2.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" 37 | 38 | [[package]] 39 | name = "digest" 40 | version = "0.7.6" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" 43 | dependencies = [ 44 | "generic-array", 45 | ] 46 | 47 | [[package]] 48 | name = "fake-simd" 49 | version = "0.1.2" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 52 | 53 | [[package]] 54 | name = "generic-array" 55 | version = "0.9.1" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "6d00328cedcac5e81c683e5620ca6a30756fc23027ebf9bff405c0e8da1fbb7e" 58 | dependencies = [ 59 | "typenum", 60 | ] 61 | 62 | [[package]] 63 | name = "itoa" 64 | version = "1.0.3" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" 67 | 68 | [[package]] 69 | name = "libc" 70 | version = "0.2.132" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" 73 | 74 | [[package]] 75 | name = "proc-macro2" 76 | version = "1.0.43" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" 79 | dependencies = [ 80 | "unicode-ident", 81 | ] 82 | 83 | [[package]] 84 | name = "quote" 85 | version = "1.0.21" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 88 | dependencies = [ 89 | "proc-macro2", 90 | ] 91 | 92 | [[package]] 93 | name = "ryu" 94 | version = "1.0.11" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 97 | 98 | [[package]] 99 | name = "serde" 100 | version = "1.0.143" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" 103 | 104 | [[package]] 105 | name = "serde_derive" 106 | version = "1.0.143" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" 109 | dependencies = [ 110 | "proc-macro2", 111 | "quote", 112 | "syn", 113 | ] 114 | 115 | [[package]] 116 | name = "serde_json" 117 | version = "1.0.83" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" 120 | dependencies = [ 121 | "itoa", 122 | "ryu", 123 | "serde", 124 | ] 125 | 126 | [[package]] 127 | name = "sha2" 128 | version = "0.7.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" 131 | dependencies = [ 132 | "block-buffer", 133 | "byte-tools", 134 | "digest", 135 | "fake-simd", 136 | ] 137 | 138 | [[package]] 139 | name = "syn" 140 | version = "1.0.99" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" 143 | dependencies = [ 144 | "proc-macro2", 145 | "quote", 146 | "unicode-ident", 147 | ] 148 | 149 | [[package]] 150 | name = "time" 151 | version = "0.1.44" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 154 | dependencies = [ 155 | "libc", 156 | "wasi", 157 | "winapi", 158 | ] 159 | 160 | [[package]] 161 | name = "typenum" 162 | version = "1.15.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 165 | 166 | [[package]] 167 | name = "unicode-ident" 168 | version = "1.0.3" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" 171 | 172 | [[package]] 173 | name = "wasi" 174 | version = "0.10.0+wasi-snapshot-preview1" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 177 | 178 | [[package]] 179 | name = "winapi" 180 | version = "0.3.9" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 183 | dependencies = [ 184 | "winapi-i686-pc-windows-gnu", 185 | "winapi-x86_64-pc-windows-gnu", 186 | ] 187 | 188 | [[package]] 189 | name = "winapi-i686-pc-windows-gnu" 190 | version = "0.4.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 193 | 194 | [[package]] 195 | name = "winapi-x86_64-pc-windows-gnu" 196 | version = "0.4.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 199 | -------------------------------------------------------------------------------- /src/pow_blockchain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blockchain" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | time = "0.1.38" 10 | serde = "1.0" 11 | serde_derive = "1.0" 12 | serde_json = "1.0" 13 | sha2 = "0.7.0" -------------------------------------------------------------------------------- /src/pow_blockchain/README.md: -------------------------------------------------------------------------------- 1 | # What is a blockchain? 2 | 3 | A blockchain is a distributed, immutable, (hopefully) decentralized, peer-to-peer ledger that exists across a network of nodes. It acts as an accessible, transparent distributed database that keeps track of all transactions in blocks. If you were to enter transactions into a spreadsheet, a block would be akin to a cell. Information such as the transaction amount, sender address, recipient address, hash, and timestamp are recorded and encrypted into the blockheader - a hexadecimal number created through the blockchain's hashing function. The hash from each block is used in the subsequent block such that the ledger becomes a chain of blocks, hence the name blockchain. The information stored on previously chained blocks cannot be altered since every block is included in the newest block's hash. 4 | 5 | # What is Proof of Work (PoW)? 6 | Proof of work (PoW) is a form of cryptographic proof wherein one party (the prover) proves to the others (the verifiers) that a certain amount of a specific computational effort has been expended. With respect to blockchains, PoW is a popular consensus mechanism, popularized by Bitcoin and Ethereum, where miners compete to append blocks and mine new currency. Each miner experiences a success probability that is proportional to the computational effort that they have expended. The purpose of PoW is not proving that certain work was carried out but deterring manipulation of data by establishing large energy and hardware-control requirements to be able to do so 7 | 8 | # I thought you already coded your own blockchain? 9 | I coded [IchiChain](https://github.com/0xIchigo/IchiChain), a rudimentary Proof of Stake blockchain coded in Python, in August of 2022. Prior to starting it, I had created my own PoW blockchain written in JavaScript. This PoW blockchain is an improvement upon my original JavaScript creation within the context of learning and mastering Rust. It is meant for educational purposes only and is not production-ready code. -------------------------------------------------------------------------------- /src/pow_blockchain/src/blockchain.rs: -------------------------------------------------------------------------------- 1 | extern crate time; 2 | extern crate serde; 3 | extern crate serde_json; 4 | extern crate sha2; 5 | 6 | use self::sha2::{ Sha256, Digest }; 7 | use std::fmt::Write; 8 | 9 | #[derive(Debug, Clone, Serialize)] 10 | struct Transaction { 11 | sender: String, 12 | receiver: String, 13 | amount: f32, 14 | } 15 | 16 | #[derive(Debug, Serialize)] 17 | pub struct Blockheader { 18 | timestamp: i64, 19 | nonce: u32, 20 | pre_hash: String, 21 | merkle: String, 22 | difficulty: u32, 23 | } 24 | 25 | #[derive(Debug, Serialize)] 26 | pub struct Block { 27 | header: Blockheader, 28 | count: u32, 29 | transactions: Vec, 30 | } 31 | 32 | pub struct Chain { 33 | chain: Vec, 34 | curr_transaction: Vec, 35 | difficulty: u32, 36 | miner_addr: String, 37 | reward: f32, 38 | } 39 | 40 | impl Chain { 41 | pub fn new(miner_addr: String, difficulty: u32) -> Chain { 42 | let mut chain = Chain { 43 | chain: Vec::new(), 44 | curr_transaction: Vec::new(), 45 | difficulty, 46 | miner_addr, 47 | reward: 100.00 48 | }; 49 | 50 | chain.generate_new_block(); 51 | chain 52 | } 53 | 54 | pub fn generate_new_block(&mut self) -> bool { 55 | let header = Blockheader { 56 | timestamp: time::now().to_timespec().sec, 57 | nonce: 0, 58 | pre_hash: self.last_hash(), 59 | merkle: String::new(), 60 | difficulty: self.difficulty, 61 | }; 62 | 63 | let reward_txt = Transaction { 64 | sender: String::from("Root"), 65 | receiver: self.miner_addr.clone(), 66 | amount: self.reward, 67 | }; 68 | 69 | let mut block = Block { 70 | header, 71 | count: 0, 72 | transactions: vec![] 73 | }; 74 | 75 | block.transactions.push(reward_txt); 76 | block.transactions.append(&mut self.curr_transaction); 77 | block.count = block.transactions.len() as u32; 78 | block.header.merkle = Chain::get_merkle(block.transactions.clone()); 79 | Chain::proof_of_work(&mut block.header); 80 | 81 | println!("{:#?}", &block); 82 | self.chain.push(block); 83 | true 84 | } 85 | 86 | pub fn new_transaction(&mut self, sender: String, receiver: String, amount: f32) -> bool { 87 | self.curr_transaction.push(Transaction { 88 | sender, receiver, amount 89 | }); 90 | true 91 | } 92 | 93 | pub fn last_hash(&self) -> String { 94 | let block = match self.chain.last() { 95 | Some(block) => block, 96 | None => return String::from_utf8(vec![48; 64]).unwrap() 97 | }; 98 | 99 | Chain::hash(&block.header) 100 | } 101 | 102 | pub fn update_difficulty(&mut self, difficulty: u32) -> bool { 103 | self.difficulty = difficulty; 104 | true 105 | } 106 | 107 | pub fn update_reward(&mut self, reward: f32) -> bool { 108 | self.reward = reward; 109 | true 110 | } 111 | 112 | fn get_merkle(curr_transaction: Vec) -> String { 113 | let mut merkle = Vec::new(); 114 | for i in curr_transaction { 115 | let hash = Chain::hash(&i); 116 | merkle.push(hash); 117 | } 118 | 119 | if merkle.len() % 2 == 1 { 120 | let last = merkle.last().cloned().unwrap(); 121 | merkle.push(last); 122 | } 123 | 124 | while merkle.len() > 1 { 125 | let mut h1 = merkle.remove(0); 126 | let mut h2 = merkle.remove(0); 127 | h1.push_str(&mut h2); 128 | let nh = Chain::hash(&h1); 129 | merkle.push(nh); 130 | } 131 | 132 | merkle.pop().unwrap() 133 | } 134 | 135 | pub fn proof_of_work(header: &mut Blockheader) { 136 | loop { 137 | let hash = Chain::hash(header); 138 | let slice: &str = &hash[..header.difficulty as usize]; 139 | match slice.parse::() { 140 | Ok(val) => { 141 | if val != 0 { 142 | header.nonce += 1; 143 | } else { 144 | println!("Block hash: {}", hash); 145 | break; 146 | } 147 | }, 148 | Err(_) => { 149 | header.nonce += 1; 150 | continue 151 | } 152 | } 153 | } 154 | } 155 | 156 | pub fn hash(item: &T) -> String { 157 | let input = serde_json::to_string(&item).unwrap(); 158 | let mut hasher = Sha256::default(); 159 | 160 | hasher.input(input.as_bytes()); 161 | 162 | let res = hasher.result(); 163 | let vec_res = res.to_vec(); 164 | 165 | Chain::hex_to_string(vec_res.as_slice()) 166 | } 167 | 168 | pub fn hex_to_string(vec_res: &[u8]) -> String { 169 | let mut s = String::new(); 170 | for i in vec_res { 171 | write!(&mut s, "{:x}", i).expect("Unable to write"); 172 | } 173 | s 174 | } 175 | } -------------------------------------------------------------------------------- /src/pow_blockchain/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | 4 | use std::io; 5 | use std::process; 6 | use std::io::{ Write }; 7 | 8 | mod blockchain; 9 | 10 | fn main() { 11 | let mut miner_addr = String::new(); 12 | let mut difficulty = String::new(); 13 | let mut choice = String::new(); 14 | 15 | print!("Input a miner address: "); 16 | io::stdout().flush(); 17 | io::stdin().read_line(&mut miner_addr); 18 | print!("Input a difficulty: "); 19 | io::stdout().flush(); 20 | io::stdin().read_line(&mut difficulty); 21 | 22 | match difficulty.trim().parse::() { 23 | Ok(_) => { 24 | println!("Generating genesis block..."); 25 | }, 26 | Err(_) => { 27 | println!("Invalid input! Difficulty must be a whole number"); 28 | println!("Failed to generate genesis block..."); 29 | println!("Exiting..."); 30 | process::exit(0); 31 | } 32 | } 33 | 34 | let mut chain = blockchain::Chain::new(miner_addr.trim().to_string(), difficulty.trim().parse::().unwrap()); 35 | 36 | loop { 37 | println!("~~~"); 38 | println!("Menu: "); 39 | println!("1. New transaction"); 40 | println!("2. Mine block"); 41 | println!("3. Change difficulty"); 42 | println!("4. Change reward"); 43 | println!("0. Exit"); 44 | println!("~~~"); 45 | 46 | print!("Please enter a number: "); 47 | io::stdout().flush(); 48 | choice.clear(); 49 | io::stdin().read_line(&mut choice); 50 | 51 | match choice.trim().parse::() { 52 | Ok(choice) => { 53 | match choice { 54 | 0 => { 55 | print!("Exiting..."); 56 | process::exit(0); 57 | }, 58 | 1 => { 59 | let mut sender = String::new(); 60 | let mut receiver = String::new(); 61 | let mut amount = String::new(); 62 | 63 | print!("Enter sender address: "); 64 | io::stdout().flush(); 65 | io::stdin().read_line(&mut sender).expect("Error reading user input"); 66 | print!("Enter receiver address: "); 67 | io::stdout().flush(); 68 | io::stdin().read_line(&mut receiver).expect("Error reading user input"); 69 | print!("Enter amount: "); 70 | io::stdout().flush(); 71 | io::stdin().read_line(&mut amount).expect("Error reading user input"); 72 | 73 | match amount.trim().parse::() { 74 | Ok(amount) => { 75 | let res = chain.new_transaction(sender.trim().to_string(), receiver.trim().to_string(), amount); 76 | 77 | match res { 78 | true => println!("Transaction added"), 79 | false => println!("Transaction failed") 80 | } 81 | }, 82 | Err(_) => println!("Invalid! Please enter a valid, numeric amount") 83 | } 84 | }, 85 | 2 => { 86 | print!("Generating block..."); 87 | let res = chain.generate_new_block(); 88 | match res { 89 | true => println!("Block generated successfully"), 90 | false => println!("Block generation failed"), 91 | } 92 | }, 93 | 3 => { 94 | let mut new_diff = String::new(); 95 | print!("Please enter new difficulty: "); 96 | io::stdout().flush(); 97 | io::stdin().read_line(&mut new_diff); 98 | 99 | let res = chain.update_difficulty(new_diff.trim().parse().unwrap()); 100 | match res { 101 | true => println!("Updated difficulty"), 102 | false => println!("Failed update"), 103 | } 104 | println!("~~~"); 105 | }, 106 | 4 => { 107 | let mut new_reward = String::new(); 108 | println!("Please enter new reward: "); 109 | io::stdout().flush(); 110 | io::stdin().read_line(&mut new_reward); 111 | 112 | let res = chain.update_reward(new_reward.trim().parse().unwrap()); 113 | match res { 114 | true => println!("Updated reward"), 115 | false => println!("Failed update"), 116 | } 117 | println!("~~~"); 118 | }, 119 | _ => print!("Invalid choice. Please try again"), 120 | } 121 | }, 122 | Err(_) => { 123 | println!("Invalid input! Please enter a numeric value from 0 to 4"); 124 | } 125 | }; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/quick_sort.rs: -------------------------------------------------------------------------------- 1 | // Quick sort is a divide-and-conquer sorting algorithm that works by selecting a pivot, or a key, element from 2 | // the array and patrtitions the other elements into two sub-arrays depending on whether they are less than or 3 | // greater than the key element. These sub-arrays are then sorted recursively 4 | 5 | // In terms of runtime complexity, a quick sort makes at most O(n^2) comparisons where n is the number of elements 6 | // in the array 7 | 8 | pub fn quick_sort(array: &mut [T]) -> Vec { 9 | if array.is_empty() { 10 | return vec![]; 11 | } 12 | 13 | let length = array.len(); 14 | q_s(array, 0, (length - 1) as isize); 15 | array.to_vec() 16 | } 17 | 18 | pub fn q_s(array: &mut [T], low: isize, high: isize) { 19 | if low < high { 20 | let p = partrition(array, low, high); 21 | 22 | q_s(array, low, p - 1); 23 | q_s(array, p + 1, high); 24 | } 25 | } 26 | 27 | pub fn partrition(array: &mut [T], low: isize, high: isize) -> isize { 28 | let key = high as usize; 29 | let mut index = low - 1; 30 | let mut last_index = high; 31 | 32 | loop { 33 | index += 1; 34 | 35 | while array[index as usize] < array[key] { 36 | index += 1; 37 | } 38 | 39 | last_index -= 1; 40 | 41 | while last_index >= 0 && array[last_index as usize] > array[key] { 42 | last_index -= 1; 43 | } 44 | 45 | if index >= last_index { 46 | break; 47 | } else { 48 | array.swap(index as usize, last_index as usize); 49 | } 50 | } 51 | 52 | array.swap(index as usize, key as usize); 53 | index 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use super::*; 59 | 60 | #[test] 61 | fn test_quick_sort() { 62 | 63 | let mut test1 = [12, 39, 4, 36, 777]; 64 | assert_eq!(quick_sort(&mut test1), [4, 12, 36, 39, 777]); 65 | 66 | let mut test2 = [21, 55, 14, -123, 32, 0]; 67 | assert_eq!(quick_sort(&mut test2), [-123, 0, 14, 21, 32, 55]); 68 | 69 | let mut test3 = ["Orange", "Pear", "Apple", "Grape", "Banana"]; 70 | assert_eq!(quick_sort(&mut test3), ["Apple", "Banana", "Grape", "Orange", "Pear"]); 71 | } 72 | } -------------------------------------------------------------------------------- /src/selection_sort.rs: -------------------------------------------------------------------------------- 1 | // Selection sort is an in-place comparison sorting algorithm that sorts an array by 2 | // repeatedly finding the minimum element from the unsorted part of the array and putting 3 | // it at the beginning. Thus, in every iteration of the selection sort, the minimum 4 | // element from the unsorted subarray is picked and moved to the sorted subarray 5 | 6 | // In terms of runtime complexity, a selection sort makes at most O(n^2) comparisons 7 | // making it rather inefficient on larger lists and generally performs worse than 8 | // insertion sort, a similar sorting algorithm. It is, however, noted for its simplicity and 9 | // has performance advantages over more complicated algorithms in certain situations, 10 | // particularly where auxiliary memory is limited 11 | 12 | fn selection_sort(array: &mut [T]) -> Vec { 13 | if array.len() == 0 { 14 | return vec![]; 15 | } 16 | 17 | let (minimum_index, _) = array.iter().enumerate().min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()).unwrap(); 18 | array.swap(0, minimum_index); 19 | 20 | if array.len() > 1 { 21 | selection_sort(&mut array[1..]); 22 | } 23 | 24 | array.to_vec() 25 | } 26 | 27 | #[cfg(test)] 28 | mod tests { 29 | use super::*; 30 | 31 | #[test] 32 | fn test_selection_sort() { 33 | 34 | let mut test1 = vec![12, 39, 4, 36, 777]; 35 | assert_eq!(selection_sort(&mut test1), vec![4, 12, 36, 39, 777]); 36 | 37 | let mut test2 = vec![21, 55, 14, -123, 32, 0]; 38 | assert_eq!(selection_sort(&mut test2), vec![-123, 0, 14, 21, 32, 55]); 39 | 40 | let mut test3 = vec!["Orange", "Pear", "Apple", "Grape", "Banana"]; 41 | assert_eq!(selection_sort(&mut test3), vec!["Apple", "Banana", "Grape", "Orange", "Pear"]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/shell_sort.rs: -------------------------------------------------------------------------------- 1 | // Shell sort, also known as Shell's method, is an in-place comparison sorting algorithm 2 | // seen as a more generalized version of the insertion sort algorithm. The algorithm works 3 | // by sorting pair elements far apart from one another, and reduces the gap between elements 4 | // to be compared 5 | 6 | // In terms of time complexity, a shell sort makes at most O(n^2) where n is the number 7 | // of comparisons. This, however, is subject to contenscious debate as the runtime of a shell sort 8 | // algorithm depends on the gap sequenced; the best known gap sequence has a worst-case 9 | // performance of O(nlog n) comparisons 10 | 11 | pub fn shell_sort(array: &mut [T]) -> Vec { 12 | if array.is_empty() { 13 | return vec![]; 14 | } 15 | 16 | let length = array.len(); 17 | let mut gap = length / 2; 18 | 19 | while gap > 0 { 20 | for i in gap..length { 21 | let temp = array[i].clone(); 22 | let mut j = i.clone(); 23 | while j >= gap && array[j - gap] > temp { 24 | array[j] = array[j - gap].clone(); 25 | j -= gap; 26 | } 27 | array[j] = temp; 28 | } 29 | gap /= 2; 30 | } 31 | 32 | array.to_vec() 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | 39 | #[test] 40 | fn test_shell_sort() { 41 | 42 | let mut test1 = vec![12, 39, 4, 36, 777]; 43 | assert_eq!(shell_sort(&mut test1), vec![4, 12, 36, 39, 777]); 44 | 45 | let mut test2 = vec![21, 55, 14, -123, 32, 0]; 46 | assert_eq!(shell_sort(&mut test2), vec![-123, 0, 14, 21, 32, 55]); 47 | 48 | let mut test3 = vec!["Orange", "Pear", "Apple", "Grape", "Banana"]; 49 | assert_eq!(shell_sort(&mut test3), vec!["Apple", "Banana", "Grape", "Orange", "Pear"]); 50 | } 51 | } -------------------------------------------------------------------------------- /src/sleep_sort.rs: -------------------------------------------------------------------------------- 1 | // Sleep sort is crowned the "King of Laziness" when it comes to sorting as it sorts while sleeping. 2 | // The sorting algorithm works by starting a separate task for each item to be sorted, where each 3 | // task sleeps for an interval corresponding to the item's sort key, then emits the item. Items are 4 | // then collected sequentially in time. Sleep sort was first proposed anonymously on 4chan and later 5 | // discussed on Hacker News and Reddit 6 | 7 | // In terms of runtime complexity, a sleeping sort makes at most O(hightest_value_in_input + n) where 8 | // the algorithm sleeps for n units of time for each element all dependent upon the highest value 9 | // inputed by the user 10 | 11 | // This algorithm is different from the other sorting algorithms as, instead of having tests, it prints 12 | // out the sorted list line by line as traditionally outlined in the original bash script via 4chan 13 | 14 | use std::thread; 15 | 16 | fn sleep_sort>(nums: I) { 17 | let threads: Vec<_> = nums.map(|n| thread::spawn(move || { 18 | thread::sleep_ms(n); 19 | println!("{}", n); 20 | })).collect(); 21 | 22 | for t in threads { t.join(); } 23 | } 24 | 25 | fn main() { 26 | sleepsort(std::env::args().skip(1).map(|s| s.parse().unwrap())); 27 | } -------------------------------------------------------------------------------- /src/two_thousand.rs: -------------------------------------------------------------------------------- 1 | // An algorithm that adds all positive integers from 1 to 2000 to a text file named numbers.txt 2 | //...because why not! 3 | 4 | use std::fs::OpenOptions; 5 | use std::io::{ BufWriter, Write }; 6 | 7 | fn main() { 8 | let file = OpenOptions::new() 9 | .write(true) 10 | .append(true) 11 | .open("numbers.txt") 12 | .expect("Unable to open numbers.txt"); 13 | 14 | let mut f = BufWriter::new(file); 15 | 16 | for i in 1..2001 { 17 | if i % 50 == 0 { write!(f, "\n") 18 | .expect("Unable to write to numbers.txt"); } 19 | write!(f, "{}", i).expect("Unable to write to numbers.txt"); 20 | } 21 | } --------------------------------------------------------------------------------