├── .gitignore ├── accumulate ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── accumulate.rs ├── acronym ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── acronym.rs ├── all-your-base ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── all-your-base.rs ├── allergies ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── allergies.rs ├── alphametics ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── alphametics.rs ├── anagram ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── anagrams.rs ├── armstrong-numbers ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── armstrong-numbers.rs ├── atbash-cipher ├── Cargo.toml ├── README.md ├── src │ ├── lib.rs │ └── main.rs └── tests │ └── atbash-cipher.rs ├── beer-song ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── beer-song.rs ├── binary-search ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── binary-search.rs ├── bob ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── bob.rs ├── book-store ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── book-store.rs ├── bowling ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ ├── lib.rs │ └── scoring.rs └── tests │ └── bowling.rs ├── bracket-push ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── bracket-push.rs ├── circular-buffer ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── circular-buffer.rs ├── clock ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── clock.rs ├── collatz-conjecture ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── collatz-conjecture.rs ├── crypto-square ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── crypto-square.rs ├── custom-set ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── custom-set.rs ├── decimal ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── decimal.rs ├── diamond ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── diamond.rs ├── difference-of-squares ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── difference-of-square.rs ├── diffie-hellman ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── diffie-hellman.rs ├── dominoes ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── dominoes.rs ├── dot-dsl ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── dot-dsl.rs ├── etl ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── etl.rs ├── forth ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── forth.rs ├── gigasecond ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── gigasecond.rs ├── grade-school ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── grade-school.rs ├── grains ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── grains.rs ├── grep ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── grep.rs ├── hamming ├── Cargo.toml ├── README.md ├── src │ ├── lib.rs │ └── main.rs └── tests │ └── hamming.rs ├── isbn-verifier ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── isbn-verifier.rs ├── isogram ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── isogram.rs ├── largest-series-product ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── largest-series-product.rs ├── leap ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── leap.rs ├── luhn-from ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── luhn-from.rs ├── luhn-trait ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── luhn-trait.rs ├── luhn ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── luhn.rs ├── macros ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── macros.rs ├── minesweeper ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── minesweeper.rs ├── nth-prime ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── nth-prime.rs ├── nucleotide-count ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── nucleotide-count.rs ├── ocr-numbers ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── ocr-numbers.rs ├── palindrome-products ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── palindrome-products.rs ├── pangram ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── pangram.rs ├── parallel-letter-frequency ├── Cargo.toml ├── README.md ├── benches │ └── benchmark.rs ├── examples_ │ ├── message_passing.rs │ ├── rayon.rs │ └── thread_handles.rs ├── src │ └── lib.rs └── tests │ └── parallel-letter-freqency.rs ├── pascals-triangle ├── Cargo.toml ├── README.md ├── src │ ├── lib.rs │ └── main.rs └── tests │ └── pascals-triangle.rs ├── perfect-numbers ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── perfect-numbers.rs ├── phone-number ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── phone-number.rs ├── pig-latin ├── .gitignore ├── .vscode │ └── launch.json ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── pig-latin.rs ├── poker ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ ├── card.rs │ ├── hand.rs │ └── lib.rs └── tests │ └── poker.rs ├── prime-factors ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── prime-factors.rs ├── protein-translation ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── proteins.rs ├── proverb ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── proverb.rs ├── pythagorean-triplet ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── pythagorean-triplet.rs ├── queen-attack ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── queen-attack.rs ├── raindrops ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── raindrops.rs ├── reverse-string ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── reverse-string.rs ├── rna-transcription ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── rna-transcription.rs ├── robot-name ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── robot-name.rs ├── robot-simulator ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── robot-simulator.rs ├── roman-numerals ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── roman-numerals.rs ├── rotational-cipher ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── rotational-cipher.rs ├── run-length-encoding ├── Cargo.lock ├── Cargo.toml ├── README.md ├── src │ ├── lib.rs │ └── main.rs └── tests │ └── run-length-encoding.rs ├── saddle-points ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── saddle-points.rs ├── say ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── say.rs ├── scrabble-score ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── scrabble-score.rs ├── series ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── series.rs ├── sieve ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── sieve.rs ├── simple-cipher ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── simple-cipher.rs ├── simple-linked-list ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── simple-linked-list.rs ├── space-age ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── space-age.rs ├── spiral-matrix ├── .vscode │ └── launch.json ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── spiral-matrix.rs ├── sublist ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── sublist.rs ├── sum-of-multiples ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── sum-of-multiples.rs ├── tournament ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── tournament.rs ├── triangle ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── triangle.rs ├── two-bucket ├── Cargo.toml ├── README.md ├── src │ ├── bucket.rs │ ├── buckets.rs │ └── lib.rs └── tests │ ├── bucket_tests.rs │ └── two-bucket.rs ├── twofer ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── two-fer.rs ├── variable-length-quantity ├── .gitignore ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── variable-length-quantity.rs ├── word-count ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── word-count.rs └── wordy ├── .gitignore ├── Cargo.toml ├── README.md ├── src └── lib.rs └── tests └── wordy.rs /.gitignore: -------------------------------------------------------------------------------- 1 | */target 2 | *.lock 3 | .solution.json 4 | .gitignore -------------------------------------------------------------------------------- /accumulate/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /accumulate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "accumulate" 3 | version = "0.0.0" 4 | authors = ["sacherjj "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /accumulate/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn map_function i32>(input: Vec, f: F) -> Vec { 2 | let mut output = vec![]; 3 | for value in input { 4 | output.push(f(value)) 5 | } 6 | output 7 | } 8 | 9 | pub fn map_closure i32>(input: Vec, f: F) -> Vec { 10 | map_function(input, f) 11 | } 12 | -------------------------------------------------------------------------------- /acronym/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "acronym" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | regex = "0.2" -------------------------------------------------------------------------------- /acronym/README.md: -------------------------------------------------------------------------------- 1 | # Acronym 2 | 3 | Convert a long phrase to its acronym 4 | 5 | Techies love their TLA (Three Letter Acronyms)! 6 | 7 | Help generate some jargon by writing a program that converts a long name 8 | like Portable Network Graphics to its acronym (PNG). 9 | 10 | 11 | ## Rust Installation 12 | 13 | Refer to the [exercism help page][help-page] for Rust installation and learning 14 | resources. 15 | 16 | ## Writing the Code 17 | 18 | Execute the tests with: 19 | 20 | ```bash 21 | $ cargo test 22 | ``` 23 | 24 | All but the first test have been ignored. After you get the first test to 25 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 26 | to pass again. The test file is located in the `tests` directory. You can 27 | also remove the ignore flag from all the tests to get them to run all at once 28 | if you wish. 29 | 30 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 31 | haven't already, it will help you with organizing your files. 32 | 33 | ## Feedback, Issues, Pull Requests 34 | 35 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 36 | 37 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 38 | 39 | [help-page]: http://exercism.io/languages/rust 40 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 41 | 42 | ## Source 43 | 44 | Julien Vanier [https://github.com/monkbroc](https://github.com/monkbroc) 45 | 46 | ## Submitting Incomplete Problems 47 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 48 | 49 | -------------------------------------------------------------------------------- /acronym/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | use regex::Regex; 3 | 4 | pub fn abbreviate(text: &str) -> String { 5 | let re = Regex::new(r"\b\w|[A-Z][a-z]").unwrap(); 6 | let mut result: Vec = Vec::new(); 7 | for caps in re.captures_iter(text) { 8 | result.push(caps[0][..1].into()); 9 | } 10 | result.join("").to_uppercase() 11 | } 12 | -------------------------------------------------------------------------------- /acronym/tests/acronym.rs: -------------------------------------------------------------------------------- 1 | extern crate acronym; 2 | 3 | #[test] 4 | fn basic() { 5 | assert_eq!(acronym::abbreviate("Portable Network Graphics"), "PNG"); 6 | } 7 | 8 | #[test] 9 | fn lowercase_words() { 10 | assert_eq!(acronym::abbreviate("Ruby on Rails"), "ROR"); 11 | } 12 | 13 | #[test] 14 | fn camelcase() { 15 | assert_eq!(acronym::abbreviate("HyperText Markup Language"), "HTML"); 16 | } 17 | 18 | #[test] 19 | fn punctuation() { 20 | assert_eq!(acronym::abbreviate("First In, First Out"), "FIFO"); 21 | } 22 | 23 | #[test] 24 | fn all_caps_words() { 25 | assert_eq!(acronym::abbreviate("PHP: Hypertext Preprocessor"), "PHP"); 26 | } 27 | 28 | #[test] 29 | fn non_acronym_all_caps_word() { 30 | assert_eq!(acronym::abbreviate("GNU Image Manipulation Program"), "GIMP"); 31 | } 32 | 33 | #[test] 34 | fn hyphenated() { 35 | assert_eq!(acronym::abbreviate("Complementary metal-oxide semiconductor"), 36 | "CMOS"); 37 | } 38 | -------------------------------------------------------------------------------- /all-your-base/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "allyourbase" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /all-your-base/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn convert(number: &[u32], from_base: u32, to_base: u32) -> Result, String> { 2 | from(number, from_base).and_then(|num| to(num, to_base)) 3 | } 4 | 5 | fn from(number: &[u32], base: u32) -> Result { 6 | let is_valid = |digits: &[u32], base| digits.iter().all(|x| x < base); 7 | 8 | if !is_valid(number, &base) || base < 2 { 9 | return Err("Invalid parameters!".into()); 10 | } 11 | Ok(number.iter().fold(0, |acc, value| acc * base + value)) 12 | } 13 | 14 | fn to(mut number: u32, base: u32) -> Result, String> { 15 | if base < 2 { 16 | return Err("Invalid parameters!".into()); 17 | } 18 | let mut output = Vec::new(); 19 | while number > 0 { 20 | let (quot, rem) = (number / base, number % base); 21 | output.push(rem); 22 | number = quot 23 | } 24 | Ok(output.into_iter().rev().collect()) 25 | } 26 | -------------------------------------------------------------------------------- /allergies/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /allergies/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "allergies" 3 | version = "1.1.0" 4 | 5 | -------------------------------------------------------------------------------- /allergies/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, Debug, PartialEq)] 2 | pub enum Allergen { 3 | Eggs = 0, 4 | Peanuts, 5 | Shellfish, 6 | Strawberries, 7 | Tomatoes, 8 | Chocolate, 9 | Pollen, 10 | Cats, 11 | } 12 | 13 | use Allergen::*; 14 | 15 | static ALLERGENS: [Allergen; 8] = [ 16 | Eggs, 17 | Peanuts, 18 | Shellfish, 19 | Strawberries, 20 | Tomatoes, 21 | Chocolate, 22 | Pollen, 23 | Cats, 24 | ]; 25 | 26 | pub struct Allergies { 27 | allergies: Vec, 28 | } 29 | 30 | impl Allergies { 31 | pub fn new(input: u32) -> Self { 32 | let mut allergies = vec![]; 33 | for allergen in &ALLERGENS { 34 | let value = 1 << *allergen as u32; 35 | if value & input > 0 { 36 | allergies.push(*allergen); 37 | } 38 | } 39 | Allergies { allergies } 40 | } 41 | pub fn is_allergic_to(&self, allergen: &Allergen) -> bool { 42 | self.allergies.contains(allergen) 43 | } 44 | 45 | pub fn allergies(self) -> Vec { 46 | self.allergies 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /alphametics/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /alphametics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "alphametics" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | itertools = "0.7" 7 | permutohedron = "0.2" -------------------------------------------------------------------------------- /anagram/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anagram" 3 | version = "0.0.0" -------------------------------------------------------------------------------- /anagram/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | pub fn anagrams_for<'a>(word: &str, possible_anagrams: &'a [&str]) -> HashSet<&'a str> { 4 | possible_anagrams 5 | .iter() 6 | .cloned() 7 | .filter(|&anagram| anagram.to_lowercase() != word.to_lowercase() && compare(anagram, word)) 8 | .collect() 9 | } 10 | 11 | fn compare(left: &str, right: &str) -> bool { 12 | let mut right = right 13 | .chars() 14 | .map(|chr| chr.to_lowercase().to_string()) 15 | .collect::>(); 16 | let mut left = left 17 | .chars() 18 | .map(|chr| chr.to_lowercase().to_string()) 19 | .collect::>(); 20 | left.sort(); 21 | right.sort(); 22 | left == right 23 | } 24 | -------------------------------------------------------------------------------- /armstrong-numbers/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Cargo.lock if creating a library 2 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /armstrong-numbers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "armstrong_numbers" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /armstrong-numbers/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn is_armstrong_number(num: u32) -> bool { 2 | let stringified = num.to_string(); 3 | let len = stringified.len(); 4 | let value: u32 = stringified 5 | .chars() 6 | .map(|chr| chr.to_digit(10).unwrap().pow(len as u32)) 7 | .sum(); 8 | value == num 9 | } 10 | -------------------------------------------------------------------------------- /armstrong-numbers/tests/armstrong-numbers.rs: -------------------------------------------------------------------------------- 1 | extern crate armstrong_numbers; 2 | use armstrong_numbers::*; 3 | 4 | #[test] 5 | fn test_single_digit_numbers_are_armstrong_numbers() { 6 | assert!(is_armstrong_number(5)) 7 | } 8 | 9 | #[test] 10 | fn test_there_are_no_2_digit_armstring_numbers() { 11 | assert!(!is_armstrong_number(10)) 12 | } 13 | 14 | #[test] 15 | fn test_three_digit_armstrong_number() { 16 | assert!(is_armstrong_number(153)) 17 | } 18 | 19 | #[test] 20 | fn test_three_digit_non_armstrong_number() { 21 | assert!(!is_armstrong_number(100)) 22 | } 23 | 24 | #[test] 25 | fn test_four_digit_armstrong_number() { 26 | assert!(is_armstrong_number(9474)) 27 | } 28 | 29 | #[test] 30 | fn test_four_digit_non_armstrong_number() { 31 | assert!(!is_armstrong_number(9475)) 32 | } 33 | 34 | #[test] 35 | fn test_seven_digit_armstrong_number() { 36 | assert!(is_armstrong_number(9926315)) 37 | } 38 | 39 | #[test] 40 | fn test_seven_digit_non_armstrong_number() { 41 | assert!(!is_armstrong_number(9926316)) 42 | } 43 | -------------------------------------------------------------------------------- /atbash-cipher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "atbash-cipher" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /atbash-cipher/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | pub fn encode(text: &str) -> String { 3 | text.to_lowercase() 4 | .chars() 5 | .filter(|&ch| ch.is_alphanumeric() && ch.is_ascii()) 6 | .map(|ch| convert(ch).to_string()) 7 | .collect::>() 8 | .chunks(5) 9 | .map(|block| block.join("") ) 10 | .collect::>() 11 | .join(" ") 12 | } 13 | 14 | pub fn decode(text: &str) -> String { 15 | text.replace(" ", "") 16 | .chars() 17 | .map(convert) 18 | .collect() 19 | } 20 | 21 | fn convert(chr: char) -> char { 22 | let (a, z, c) = (b'a', b'z', chr as u8); 23 | 24 | match chr { 25 | 'a'...'z' => (z - c + a) as char, 26 | _ => chr, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /atbash-cipher/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate atbash_cipher as cipher; 2 | 3 | fn main() { 4 | let result = cipher::encode("Testing, testing."); 5 | println!("{:?}", result); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /atbash-cipher/tests/atbash-cipher.rs: -------------------------------------------------------------------------------- 1 | extern crate atbash_cipher as cipher; 2 | 3 | #[test] 4 | fn test_encode_yes() { 5 | assert_eq!("bvh", cipher::encode("yes")); 6 | } 7 | 8 | #[test] 9 | fn test_encode_no() { 10 | assert_eq!("ml", cipher::encode("no")); 11 | } 12 | 13 | #[test] 14 | fn test_encode_omg() { 15 | assert_eq!("lnt", cipher::encode("OMG")); 16 | } 17 | 18 | #[test] 19 | fn test_encode_spaces() { 20 | assert_eq!("lnt", cipher::encode("O M G")); 21 | } 22 | 23 | #[test] 24 | fn test_encode_mindblowingly() { 25 | assert_eq!("nrmwy oldrm tob", cipher::encode("mindblowingly")); 26 | } 27 | 28 | #[test] 29 | fn test_encode_numbers() { 30 | assert_eq!("gvhgr mt123 gvhgr mt", 31 | cipher::encode("Testing,1 2 3, testing.")); 32 | } 33 | 34 | #[test] 35 | fn test_encode_deep_thought() { 36 | assert_eq!("gifgs rhurx grlm", cipher::encode("Truth is fiction.")); 37 | } 38 | 39 | #[test] 40 | fn test_encode_all_the_letters() { 41 | assert_eq!("gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt", 42 | cipher::encode("The quick brown fox jumps over the lazy dog.")); 43 | } 44 | 45 | #[test] 46 | fn test_encode_ignores_non_ascii() { 47 | assert_eq!("mlmzh xrrrt mlivw", cipher::encode("non ascii éignored")); 48 | } 49 | 50 | 51 | #[test] 52 | fn test_decode_exercism() { 53 | assert_eq!("exercism", cipher::decode("vcvix rhn")); 54 | } 55 | 56 | #[test] 57 | fn test_decode_a_sentence() { 58 | assert_eq!("anobstacleisoftenasteppingstone", 59 | cipher::decode("zmlyh gzxov rhlug vmzhg vkkrm thglm v")); 60 | } 61 | 62 | #[test] 63 | fn test_decode_numbers() { 64 | assert_eq!("testing123testing", cipher::decode("gvhgr mt123 gvhgr mt")); 65 | } 66 | 67 | #[test] 68 | fn test_decode_all_the_letters() { 69 | assert_eq!("thequickbrownfoxjumpsoverthelazydog", 70 | cipher::decode("gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt")); 71 | } 72 | -------------------------------------------------------------------------------- /beer-song/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "beer-song" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /beer-song/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | pub fn verse(n : i32) -> String { 3 | format!("{} of beer on the wall, {} of beer. 4 | {}, {} of beer on the wall. 5 | ", pluralise(n, true), pluralise(n, false), action(n), pluralise(n - 1, false)) 6 | } 7 | 8 | pub fn sing(start : i32, end : i32) -> String { 9 | let mut result : Vec = Vec::new(); 10 | for n in (end..=start).rev() { 11 | result.push(verse(n)); 12 | } 13 | result.join("\n") 14 | } 15 | 16 | fn pluralise(number: i32, capitalise : bool) -> String { 17 | match number { 18 | 1 => "1 bottle".into(), 19 | 0 if capitalise => "No more bottles".into(), 20 | 0 => "no more bottles".into(), 21 | -1 => "99 bottles".into(), 22 | _ => format!("{} bottles", number), 23 | } 24 | } 25 | 26 | fn action<'a>(number: i32) -> &'a str { 27 | match number { 28 | 1 => "Take it down and pass it around", 29 | 0 => "Go to the store and buy some more", 30 | _ => "Take one down and pass it around" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /beer-song/tests/beer-song.rs: -------------------------------------------------------------------------------- 1 | extern crate beer_song as beer; 2 | 3 | #[test] 4 | fn test_verse_0() { 5 | assert_eq!(beer::verse(0), "No more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n"); 6 | } 7 | 8 | #[test] 9 | fn test_verse_1() { 10 | assert_eq!(beer::verse(1), "1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n"); 11 | } 12 | 13 | #[test] 14 | fn test_verse_2() { 15 | assert_eq!(beer::verse(2), "2 bottles of beer on the wall, 2 bottles of beer.\nTake one down and pass it around, 1 bottle of beer on the wall.\n"); 16 | } 17 | 18 | #[test] 19 | fn test_verse_8() { 20 | assert_eq!(beer::verse(8), "8 bottles of beer on the wall, 8 bottles of beer.\nTake one down and pass it around, 7 bottles of beer on the wall.\n"); 21 | } 22 | 23 | #[test] 24 | fn test_song_8_6() { 25 | assert_eq!(beer::sing(8, 6), "8 bottles of beer on the wall, 8 bottles of beer.\nTake one down and pass it around, 7 bottles of beer on the wall.\n\n7 bottles of beer on the wall, 7 bottles of beer.\nTake one down and pass it around, 6 bottles of beer on the wall.\n\n6 bottles of beer on the wall, 6 bottles of beer.\nTake one down and pass it around, 5 bottles of beer on the wall.\n"); 26 | } 27 | 28 | #[test] 29 | fn test_song_3_0() { 30 | assert_eq!(beer::sing(3, 0), "3 bottles of beer on the wall, 3 bottles of beer.\nTake one down and pass it around, 2 bottles of beer on the wall.\n\n2 bottles of beer on the wall, 2 bottles of beer.\nTake one down and pass it around, 1 bottle of beer on the wall.\n\n1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n\nNo more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n"); 31 | } 32 | -------------------------------------------------------------------------------- /binary-search/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "binary-search" 3 | version = "1.1.0" 4 | authors = [""] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /binary-search/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn find(input: C, value: T) -> Option 2 | where 3 | C: AsRef<[T]>, 4 | T: Ord, 5 | { 6 | let list = input.as_ref(); 7 | let length = list.len(); 8 | if length == 0 { 9 | return None; 10 | } 11 | let mut index = length / 2; 12 | let mut start = 0; 13 | let mut end = length; 14 | 15 | while list[index] != value { 16 | if value < list[index] { 17 | end = index; 18 | } else { 19 | start = index; 20 | } 21 | let new_index = (start + end) / 2; 22 | if new_index == index { 23 | return None; 24 | } else { 25 | index = new_index; 26 | } 27 | } 28 | Some(index) 29 | } 30 | -------------------------------------------------------------------------------- /bob/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bob" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /bob/README.md: -------------------------------------------------------------------------------- 1 | # Bob 2 | 3 | Bob is a lackadaisical teenager. In conversation, his responses are very limited. 4 | 5 | Bob answers 'Sure.' if you ask him a question. 6 | 7 | He answers 'Whoa, chill out!' if you yell at him. 8 | 9 | He says 'Fine. Be that way!' if you address him without actually saying 10 | anything. 11 | 12 | He answers 'Whatever.' to anything else. 13 | 14 | ## Rust Installation 15 | 16 | Refer to the [exercism help page][help-page] for Rust installation and learning 17 | resources. 18 | 19 | ## Writing the Code 20 | 21 | Execute the tests with: 22 | 23 | ```bash 24 | $ cargo test 25 | ``` 26 | 27 | All but the first test have been ignored. After you get the first test to 28 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 29 | to pass again. The test file is located in the `tests` directory. You can 30 | also remove the ignore flag from all the tests to get them to run all at once 31 | if you wish. 32 | 33 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 34 | haven't already, it will help you with organizing your files. 35 | 36 | ## Feedback, Issues, Pull Requests 37 | 38 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 39 | 40 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 41 | 42 | [help-page]: http://exercism.io/languages/rust 43 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 44 | 45 | ## Source 46 | 47 | Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. [http://pine.fm/LearnToProgram/?Chapter=06](http://pine.fm/LearnToProgram/?Chapter=06) 48 | 49 | ## Submitting Incomplete Problems 50 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 51 | 52 | -------------------------------------------------------------------------------- /bob/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn reply(words: &str) -> &str { 2 | let is_shouting = |s: &str| s.to_uppercase() == s; 3 | 4 | match words { 5 | words if words.is_empty() => "Fine. Be that way!", 6 | words if words.ends_with('?') => "Sure.", 7 | words if is_shouting(words) => "Whoa, chill out!", 8 | _ => "Whatever.", 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /bob/tests/bob.rs: -------------------------------------------------------------------------------- 1 | extern crate bob; 2 | 3 | #[test] 4 | fn test_statement() { 5 | assert_eq!("Whatever.", bob::reply("Tom-ay-to, tom-aaaah-to.")); 6 | } 7 | 8 | #[test] 9 | fn test_shouting() { 10 | assert_eq!("Whoa, chill out!", bob::reply("WATCH OUT!")); 11 | } 12 | 13 | #[test] 14 | fn test_exclaiming() { 15 | assert_eq!("Whatever.", bob::reply("Let's go make out behind the gym!")); 16 | } 17 | 18 | #[test] 19 | fn test_asking() { 20 | assert_eq!("Sure.", bob::reply("Does this cryogenic chamber make me look fat?")); 21 | } 22 | 23 | #[test] 24 | fn test_shout_numbers() { 25 | assert_eq!("Whoa, chill out!", bob::reply("1, 2, 3 GO!")); 26 | } 27 | 28 | #[test] 29 | fn test_shout_weird_characters() { 30 | assert_eq!("Whoa, chill out!", bob::reply("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!")); 31 | } 32 | 33 | #[test] 34 | fn test_shout_without_punctuation() { 35 | assert_eq!("Whoa, chill out!", bob::reply("I HATE YOU")); 36 | } 37 | 38 | #[test] 39 | fn test_non_question_with_question_mark() { 40 | assert_eq!("Whatever.", bob::reply("Ending with ? means a question.")); 41 | } 42 | 43 | #[test] 44 | fn test_silent_treatment() { 45 | assert_eq!("Fine. Be that way!", bob::reply("")); 46 | } 47 | 48 | -------------------------------------------------------------------------------- /book-store/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "book_store" 3 | version = "1.3.0" 4 | 5 | [dependencies] -------------------------------------------------------------------------------- /book-store/src/lib.rs: -------------------------------------------------------------------------------- 1 | const DISCOUNT: [usize; 6] = [0, 0, 5, 10, 20, 25]; 2 | const BOOK_PRICE: usize = 800; 3 | 4 | fn contains_duplicates(list: &[usize]) -> bool { 5 | let mut list = list.to_vec(); 6 | while let Some(item) = list.pop() { 7 | if list.contains(&item) { 8 | return true; 9 | } 10 | } 11 | false 12 | } 13 | 14 | fn find_duplicates(list: &[usize]) -> (Vec, Vec) { 15 | let mut list = list.to_vec(); 16 | let mut duplicates = vec![]; 17 | let mut original = vec![]; 18 | while let Some(item) = list.pop() { 19 | if list.contains(&item) { 20 | duplicates.push(item); 21 | } else { 22 | original.push(item) 23 | } 24 | } 25 | duplicates.reverse(); 26 | original.reverse(); 27 | (original, duplicates) 28 | } 29 | 30 | pub fn group(list: &[usize]) -> Vec> { 31 | let mut duplicates = find_duplicates(&list); 32 | if contains_duplicates(&duplicates.1) { 33 | let mut result = vec![duplicates.0]; 34 | while contains_duplicates(&duplicates.1) { 35 | duplicates = find_duplicates(&duplicates.1); 36 | result.push(duplicates.0); 37 | } 38 | result.push(duplicates.1); 39 | result 40 | } else { 41 | vec![duplicates.0, duplicates.1] 42 | } 43 | } 44 | 45 | fn redistribute(group: &mut [usize]) { 46 | while group.contains(&5) && group.contains(&3) { 47 | let five_pos = group.iter().position(|&num| num == 5).unwrap(); 48 | let three_pos = group.iter().position(|&num| num == 3).unwrap(); 49 | group[five_pos] = 4; 50 | group[three_pos] = 4; 51 | } 52 | } 53 | 54 | fn get_price(num_books: usize) -> usize { 55 | let discount_rate = 100 - DISCOUNT[num_books]; 56 | num_books * BOOK_PRICE * discount_rate / 100 57 | } 58 | 59 | pub fn lowest_price(list: &[usize]) -> usize { 60 | let groups = group(list); 61 | let mut books: Vec = groups.iter().map(|group| group.len()).collect(); 62 | redistribute(&mut books); 63 | books 64 | .into_iter() 65 | .fold(0usize, |acc, group| acc + get_price(group)) 66 | } 67 | -------------------------------------------------------------------------------- /bowling/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock -------------------------------------------------------------------------------- /bowling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bowling" 3 | version = "1.0.0" -------------------------------------------------------------------------------- /bracket-push/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bracket-push" 3 | version = "1.1.0" 4 | -------------------------------------------------------------------------------- /bracket-push/README.md: -------------------------------------------------------------------------------- 1 | # Bracket Push 2 | 3 | Make sure the brackets and braces all match. 4 | 5 | Ensure that all the brackets and braces are matched correctly, 6 | and nested correctly. 7 | 8 | # Bracket Push in Rust 9 | 10 | Reading about these Rust topics may help you implement a solution. 11 | 12 | - Lifetimes and Structs: https://doc.rust-lang.org/book/lifetimes.html#impl-blocks 13 | - From trait: https://doc.rust-lang.org/std/convert/trait.From.html 14 | 15 | ## Rust Installation 16 | 17 | Refer to the [exercism help page][help-page] for Rust installation and learning 18 | resources. 19 | 20 | ## Writing the Code 21 | 22 | Execute the tests with: 23 | 24 | ```bash 25 | $ cargo test 26 | ``` 27 | 28 | All but the first test have been ignored. After you get the first test to 29 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 30 | to pass again. The test file is located in the `tests` directory. You can 31 | also remove the ignore flag from all the tests to get them to run all at once 32 | if you wish. 33 | 34 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 35 | haven't already, it will help you with organizing your files. 36 | 37 | ## Feedback, Issues, Pull Requests 38 | 39 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 40 | 41 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 42 | 43 | [help-page]: http://exercism.io/languages/rust 44 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 45 | 46 | ## Source 47 | 48 | Ginna Baker 49 | 50 | ## Submitting Incomplete Problems 51 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 52 | 53 | -------------------------------------------------------------------------------- /bracket-push/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub struct Brackets { 2 | text: String, 3 | } 4 | 5 | impl Brackets { 6 | pub fn from(brackets: &str) -> Self { 7 | Brackets { text: brackets.into() } 8 | } 9 | 10 | pub fn are_balanced(&self) -> bool { 11 | let mut buffer = String::from(""); 12 | 13 | for ch in self.text.chars() { 14 | match ch { 15 | '(' => buffer.push(')'), 16 | '{' => buffer.push('}'), 17 | '[' => buffer.push(']'), 18 | ')' | '}' | ']' => { 19 | if buffer.pop() != Some(ch) { 20 | return false; 21 | } 22 | } 23 | _ => (), 24 | } 25 | } 26 | buffer.is_empty() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /bracket-push/tests/bracket-push.rs: -------------------------------------------------------------------------------- 1 | extern crate bracket_push; 2 | 3 | use bracket_push::*; 4 | 5 | #[test] 6 | fn paired_square_brackets() { 7 | assert!(Brackets::from("[]").are_balanced()); 8 | } 9 | 10 | #[test] 11 | fn empty_string() { 12 | assert!(Brackets::from("").are_balanced()); 13 | } 14 | 15 | #[test] 16 | fn unpaired_brackets() { 17 | assert!(!Brackets::from("[[").are_balanced()); 18 | } 19 | 20 | #[test] 21 | fn wrong_ordered_brackets() { 22 | assert!(!Brackets::from("}{").are_balanced()); 23 | } 24 | 25 | #[test] 26 | fn wrong_closing_bracket() { 27 | assert!(!Brackets::from("{]").are_balanced()); 28 | } 29 | 30 | #[test] 31 | fn paired_with_whitespace() { 32 | assert!(Brackets::from("{ }").are_balanced()); 33 | } 34 | 35 | #[test] 36 | fn simple_nested_brackets() { 37 | assert!(Brackets::from("{[]}").are_balanced()); 38 | } 39 | 40 | #[test] 41 | fn several_paired_brackets() { 42 | assert!(Brackets::from("{}[]").are_balanced()); 43 | } 44 | 45 | #[test] 46 | fn paired_and_nested_brackets() { 47 | assert!(Brackets::from("([{}({}[])])").are_balanced()); 48 | } 49 | 50 | #[test] 51 | fn unopened_closing_brackets() { 52 | assert!(!Brackets::from("{[)][]}").are_balanced()); 53 | } 54 | 55 | #[test] 56 | fn unpaired_and_nested_brackets() { 57 | assert!(!Brackets::from("([{])").are_balanced()); 58 | } 59 | 60 | #[test] 61 | fn paired_and_wrong_nested_brackets() { 62 | assert!(!Brackets::from("[({]})").are_balanced()); 63 | } 64 | 65 | #[test] 66 | fn math_expression() { 67 | assert!(Brackets::from("(((185 + 223.85) * 15) - 543)/2").are_balanced()); 68 | } 69 | 70 | #[test] 71 | fn complex_latex_expression() { 72 | let input = "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \ 73 | \\end{array}\\right)"; 74 | assert!(Brackets::from(input).are_balanced()); 75 | } 76 | -------------------------------------------------------------------------------- /circular-buffer/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | **/*.rs.bk 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 8 | Cargo.lock 9 | -------------------------------------------------------------------------------- /circular-buffer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "circular-buffer" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /circular-buffer/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | 3 | #[derive(Debug, PartialEq)] 4 | pub enum Error { 5 | EmptyBuffer, 6 | FullBuffer, 7 | } 8 | 9 | pub struct CircularBuffer { 10 | size: usize, 11 | buffer: VecDeque, 12 | } 13 | 14 | impl CircularBuffer { 15 | pub fn new(size: usize) -> Self { 16 | CircularBuffer { 17 | size, 18 | buffer: VecDeque::with_capacity(size), 19 | } 20 | } 21 | fn is_buffer_full(&self) -> bool { 22 | self.buffer.len() >= self.size 23 | } 24 | pub fn read(&mut self) -> Result { 25 | self.buffer.pop_front().ok_or(Error::EmptyBuffer) 26 | } 27 | pub fn write(&mut self, value: T) -> Result<(), Error> { 28 | if self.is_buffer_full() { 29 | return Err(Error::FullBuffer); 30 | } 31 | self.buffer.push_back(value); 32 | Ok(()) 33 | } 34 | pub fn clear(&mut self) { 35 | self.buffer.clear(); 36 | } 37 | pub fn overwrite(&mut self, value: T) { 38 | if self.is_buffer_full() { 39 | self.buffer.pop_front(); 40 | } 41 | self.buffer.push_back(value); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /clock/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /clock/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clock" 3 | version = "1.0.0" 4 | authors = ["sacherjj "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /clock/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug, PartialEq)] 4 | pub struct Clock { 5 | total_time: i32, 6 | } 7 | 8 | impl fmt::Display for Clock { 9 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 10 | let hours = (self.total_time / 60) % 24; 11 | let minutes = self.total_time % 60; 12 | write!(f, "{:02}:{:02}", hours, minutes) 13 | } 14 | } 15 | 16 | impl Clock { 17 | pub fn new(hours: i32, minutes: i32) -> Self { 18 | let mut total_time = hours * 60 + minutes; 19 | total_time = Self::normalise(total_time, 60 * 24); 20 | Clock { total_time } 21 | } 22 | pub fn add_minutes(&mut self, minutes: i32) -> Self { 23 | let mut total_time = self.total_time + minutes; 24 | total_time = Self::normalise(total_time, 60 * 24); 25 | Clock { total_time } 26 | } 27 | fn normalise(value: i32, limit: i32) -> i32 { 28 | (value % limit + limit) % limit 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /collatz-conjecture/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "collatz_conjecture" 3 | version = "1.2.0" 4 | -------------------------------------------------------------------------------- /collatz-conjecture/src/lib.rs: -------------------------------------------------------------------------------- 1 | fn is_even(x: u64) -> bool { 2 | x % 2 == 0 3 | } 4 | 5 | fn collatz_helper(n: u64, count: u64) -> u64 { 6 | match n { 7 | 1 => count, 8 | n if is_even(n) => collatz_helper(n / 2, count + 1), 9 | _ => collatz_helper(n * 3 + 1, count + 1), 10 | } 11 | } 12 | 13 | pub fn collatz(n: u64) -> Option { 14 | match n { 15 | 0 => None, 16 | _ => Some(collatz_helper(n, 0)), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /collatz-conjecture/tests/collatz-conjecture.rs: -------------------------------------------------------------------------------- 1 | extern crate collatz_conjecture; 2 | 3 | use collatz_conjecture::*; 4 | 5 | #[test] 6 | fn test_1() { 7 | assert_eq!(Some(0), collatz(1)); 8 | } 9 | 10 | #[test] 11 | fn test_16() { 12 | assert_eq!(Some(4), collatz(16)); 13 | } 14 | 15 | #[test] 16 | fn test_12() { 17 | assert_eq!(Some(9), collatz(12)); 18 | } 19 | 20 | #[test] 21 | fn test_1000000() { 22 | assert_eq!(Some(152), collatz(1000000)); 23 | } 24 | 25 | #[test] 26 | fn test_0() { 27 | assert_eq!(None, collatz(0)); 28 | } 29 | -------------------------------------------------------------------------------- /crypto-square/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /crypto-square/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crypto-square" 3 | version = "0.1.0" 4 | authors = ["Peter Goodspeed-Niklaus "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /crypto-square/src/lib.rs: -------------------------------------------------------------------------------- 1 | fn calculate_length(value: usize) -> usize { 2 | let mut columns = 0; 3 | while columns * columns < value { 4 | columns += 1; 5 | } 6 | columns 7 | } 8 | 9 | fn pad_string(vector: &mut Vec, length: usize) { 10 | while vector.len() % length != 0 { 11 | vector.push(' '); 12 | } 13 | } 14 | 15 | pub fn encrypt(input: &str) -> String { 16 | if input.is_empty() { 17 | return "".into(); 18 | } 19 | let mut filtered: Vec = input 20 | .to_lowercase() 21 | .chars() 22 | .filter(|letter| letter.is_alphanumeric()) 23 | .collect(); 24 | 25 | let column_length = calculate_length(filtered.len()); 26 | pad_string(&mut filtered, column_length); 27 | let chunked: Vec<_> = filtered.chunks(column_length).collect(); 28 | 29 | let mut transposed = Vec::new(); 30 | for index in 0..column_length { 31 | let mut new_string = String::new(); 32 | for item in &chunked { 33 | new_string.push(item[index]); 34 | } 35 | transposed.push(new_string); 36 | } 37 | transposed.join(" ") 38 | } 39 | -------------------------------------------------------------------------------- /crypto-square/tests/crypto-square.rs: -------------------------------------------------------------------------------- 1 | extern crate crypto_square; 2 | use crypto_square::encrypt; 3 | 4 | fn test(input: &str, output: &str) { 5 | assert_eq!(&encrypt(input), output); 6 | } 7 | 8 | #[test] 9 | fn test_empty_input() { 10 | test("", "") 11 | } 12 | 13 | #[test] 14 | fn test_encrypt_also_decrypts_square() { 15 | // note that you only get the exact input back if: 16 | // 1. no punctuation 17 | // 2. even spacing 18 | // 3. all lowercase 19 | // 4. square input 20 | let example = "lime anda coco anut"; 21 | assert_eq!(example, &encrypt(&encrypt(example))); 22 | } 23 | 24 | #[test] 25 | fn test_example() { 26 | test( 27 | "If man was meant to stay on the ground, god would have given us roots.", 28 | "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau ", 29 | ) 30 | } 31 | 32 | #[test] 33 | fn test_empty_last_line() { 34 | test("congratulate", "crl oaa ntt gue") 35 | } 36 | 37 | #[test] 38 | fn test_spaces_are_reorganized() { 39 | test("abet", "ae bt"); 40 | test("a bet", "ae bt"); 41 | test(" a b e t ", "ae bt"); 42 | } 43 | 44 | #[test] 45 | fn test_everything_becomes_lowercase() { 46 | test("caSe", "cs ae"); 47 | test("cAsE", "cs ae"); 48 | test("CASE", "cs ae"); 49 | } 50 | 51 | #[test] 52 | fn test_long() { 53 | test( 54 | r#" 55 | We choose to go to the moon. 56 | 57 | We choose to go to the moon in this decade and do the other things, 58 | not because they are easy, but because they are hard, because that 59 | goal will serve to organize and measure the best of our energies and 60 | skills, because that challenge is one that we are willing to accept, 61 | one we are unwilling to postpone, and one which we intend to win, 62 | and the others, too. 63 | 64 | -- John F. Kennedy, 12 September 1962 65 | "#, 66 | &(String::from("womdbudlmecsgwdwob enooetbsenaotioihe ") 67 | + "cwotcbeeaeunolnnnr henhaecrsrsealeaf1 ocieucavugetciwnk9 " 68 | + "ohnosauerithcnhde6 sotteusteehaegitn2 eohhtseotsatptchn " 69 | + "tsiehetohatwtohee oesrethrenceopwod gtdtyhagbdhanoety " 70 | + "ooehaetaesaresih1 tgcirygnsklewtne2 ooaneaoitilweptrs " 71 | + "ttdgerazoleiaoese hoesaeleflnlrnntp etanshwaosgleedot " 72 | + "mhnoyainubeiuatoe oedtbrldreinnnojm "), 73 | ) 74 | } 75 | -------------------------------------------------------------------------------- /custom-set/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /custom-set/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "custom-set" 3 | version = "1.0.1" 4 | -------------------------------------------------------------------------------- /custom-set/README.md: -------------------------------------------------------------------------------- 1 | # Custom Set 2 | 3 | Create a custom set type. 4 | 5 | Sometimes it is necessary to define a custom data structure of some 6 | type, like a set. In this exercise you will define your own set. How it 7 | works internally doesn't matter, as long as it behaves like a set of 8 | unique elements. 9 | 10 | ## Rust Installation 11 | 12 | Refer to the [exercism help page][help-page] for Rust installation and learning 13 | resources. 14 | 15 | ## Writing the Code 16 | 17 | Execute the tests with: 18 | 19 | ```bash 20 | $ cargo test 21 | ``` 22 | 23 | All but the first test have been ignored. After you get the first test to 24 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 25 | to pass again. The test file is located in the `tests` directory. You can 26 | also remove the ignore flag from all the tests to get them to run all at once 27 | if you wish. 28 | 29 | Make sure to read the [Modules](https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html) chapter if you 30 | haven't already, it will help you with organizing your files. 31 | 32 | ## Feedback, Issues, Pull Requests 33 | 34 | The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help! 35 | 36 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md). 37 | 38 | [help-page]: http://exercism.io/languages/rust 39 | [modules]: https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html 40 | [cargo]: https://doc.rust-lang.org/book/second-edition/ch14-00-more-about-cargo.html 41 | 42 | ## Submitting Incomplete Solutions 43 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 44 | -------------------------------------------------------------------------------- /custom-set/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct CustomSet { 3 | values: Vec, 4 | } 5 | 6 | impl CustomSet 7 | where 8 | T: Clone + PartialEq, 9 | { 10 | pub fn new(input: &[T]) -> Self { 11 | let mut custom_set = CustomSet { values: vec![] }; 12 | for item in input { 13 | custom_set.add(item.clone()); 14 | } 15 | custom_set 16 | } 17 | pub fn is_empty(&self) -> bool { 18 | self.values.is_empty() 19 | } 20 | pub fn contains(&self, value: &T) -> bool { 21 | self.values.as_slice().contains(&value) 22 | } 23 | pub fn is_subset(&self, other: &Self) -> bool { 24 | if self.values.is_empty() || self.values == other.values { 25 | return true; 26 | } 27 | let set2 = other.values.as_slice(); 28 | self 29 | .values 30 | .iter() 31 | .all(|set1_value| set2.contains(set1_value)) 32 | } 33 | pub fn is_disjoint(&self, other: &Self) -> bool { 34 | self.intersection(other).is_empty() 35 | } 36 | pub fn add(&mut self, new_value: T) { 37 | if !self.values.contains(&new_value) { 38 | self.values.push(new_value); 39 | } 40 | } 41 | pub fn intersection(&self, other: &Self) -> Self { 42 | let common = CustomSet::new(&[]); 43 | let set2 = other.values.as_slice(); 44 | self 45 | .values 46 | .iter() 47 | .cloned() 48 | .fold(common, |mut acc, set1_value| { 49 | if set2.contains(&set1_value) { 50 | acc.add(set1_value); 51 | acc 52 | } else { 53 | acc 54 | } 55 | }) 56 | } 57 | pub fn difference(&self, other: &Self) -> Self { 58 | let common = CustomSet::new(&[]); 59 | let set2 = other.values.as_slice(); 60 | self 61 | .values 62 | .iter() 63 | .cloned() 64 | .fold(common, |mut acc, set1_value| { 65 | if !set2.contains(&set1_value) { 66 | acc.add(set1_value); 67 | acc 68 | } else { 69 | acc 70 | } 71 | }) 72 | } 73 | pub fn union(&self, other: &Self) -> Self { 74 | let mut values = self.values.clone(); 75 | values.extend(other.values.clone()); 76 | CustomSet { values } 77 | } 78 | } 79 | 80 | impl PartialEq for CustomSet { 81 | fn eq(&self, other: &CustomSet) -> bool { 82 | self.is_subset(other) && other.is_subset(&self) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /decimal/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | .solution.json 9 | .vscode 10 | -------------------------------------------------------------------------------- /decimal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decimal" 3 | version = "0.1.0" 4 | 5 | [dependencies] 6 | num-bigint = "0.2.0" 7 | -------------------------------------------------------------------------------- /diamond/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "diamond" 3 | version = "1.1.0" 4 | authors = ["Kirill Meng "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /diamond/src/lib.rs: -------------------------------------------------------------------------------- 1 | fn get_template_string(chr: char) -> (String, Vec) { 2 | let alphabet: Vec = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 3 | .chars() 4 | .map(|letter| letter.to_string()) 5 | .collect(); 6 | let letter_position = alphabet 7 | .iter() 8 | .position(|letter| letter == &chr.to_string()) 9 | .unwrap(); 10 | let forward_pattern = &alphabet[0..=letter_position]; 11 | let reverse_pattern: Vec = forward_pattern.iter().cloned().skip(1).rev().collect(); 12 | let pattern = reverse_pattern.join("").to_string() + &forward_pattern.join(""); 13 | (pattern, forward_pattern.to_vec()) 14 | } 15 | 16 | pub fn get_diamond(letter: char) -> Vec { 17 | let (template, forward_pattern) = get_template_string(letter); 18 | let mut top = vec![]; 19 | for letter in forward_pattern { 20 | let filtered: String = template 21 | .chars() 22 | .map(move |chr| { 23 | let chr = chr.to_string(); 24 | if chr == letter { 25 | chr 26 | } else { 27 | " ".to_string() 28 | } 29 | }) 30 | .collect(); 31 | top.push(filtered); 32 | } 33 | let mut diamond = top.clone(); 34 | let bottom: Vec = top.iter().cloned().rev().skip(1).collect(); 35 | diamond.extend(bottom); 36 | diamond 37 | } 38 | -------------------------------------------------------------------------------- /difference-of-squares/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "difference-of-squares" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /difference-of-squares/README.md: -------------------------------------------------------------------------------- 1 | # Difference Of Squares 2 | 3 | Find the difference between the square of the sum and the sum of the squares of the first N natural numbers. 4 | 5 | The square of the sum of the first ten natural numbers is 6 | (1 + 2 + ... + 10)² = 55² = 3025. 7 | 8 | The sum of the squares of the first ten natural numbers is 9 | 1² + 2² + ... + 10² = 385. 10 | 11 | Hence the difference between the square of the sum of the first 12 | ten natural numbers and the sum of the squares of the first ten 13 | natural numbers is 3025 - 385 = 2640. 14 | 15 | ## Rust Installation 16 | 17 | Refer to the [exercism help page][help-page] for Rust installation and learning 18 | resources. 19 | 20 | ## Writing the Code 21 | 22 | Execute the tests with: 23 | 24 | ```bash 25 | $ cargo test 26 | ``` 27 | 28 | All but the first test have been ignored. After you get the first test to 29 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 30 | to pass again. The test file is located in the `tests` directory. You can 31 | also remove the ignore flag from all the tests to get them to run all at once 32 | if you wish. 33 | 34 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 35 | haven't already, it will help you with organizing your files. 36 | 37 | ## Feedback, Issues, Pull Requests 38 | 39 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 40 | 41 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 42 | 43 | [help-page]: http://exercism.io/languages/rust 44 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 45 | 46 | ## Source 47 | 48 | Problem 6 at Project Euler [http://projecteuler.net/problem=6](http://projecteuler.net/problem=6) 49 | 50 | ## Submitting Incomplete Problems 51 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 52 | 53 | -------------------------------------------------------------------------------- /difference-of-squares/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn difference(number: usize) -> usize { 2 | square_of_sum(number) - sum_of_squares(number) 3 | } 4 | 5 | pub fn square_of_sum(number: usize) -> usize { 6 | let value: usize = (1..=number).sum(); 7 | value * value 8 | } 9 | 10 | pub fn sum_of_squares(number: usize) -> usize { 11 | (1..=number).map(|x| x * x).sum() 12 | } 13 | -------------------------------------------------------------------------------- /difference-of-squares/tests/difference-of-square.rs: -------------------------------------------------------------------------------- 1 | extern crate difference_of_squares as squares; 2 | 3 | #[test] 4 | fn test_square_of_sum_5() { 5 | assert_eq!(225, squares::square_of_sum(5)); 6 | } 7 | 8 | #[test] 9 | fn test_sum_of_squares_5() { 10 | assert_eq!(55, squares::sum_of_squares(5)); 11 | } 12 | 13 | #[test] 14 | fn test_difference_5() { 15 | assert_eq!(170, squares::difference(5)); 16 | } 17 | 18 | #[test] 19 | fn test_square_of_sum_100() { 20 | assert_eq!(25502500, squares::square_of_sum(100)); 21 | } 22 | 23 | #[test] 24 | fn test_sum_of_squares_100() { 25 | assert_eq!(338350, squares::sum_of_squares(100)); 26 | } 27 | 28 | #[test] 29 | fn test_difference_100() { 30 | assert_eq!(25164150, squares::difference(100)); 31 | } 32 | -------------------------------------------------------------------------------- /diffie-hellman/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | **/*.rs.bk 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 8 | Cargo.lock 9 | 10 | -------------------------------------------------------------------------------- /diffie-hellman/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "diffie-hellman" 3 | version = "0.1.0" 4 | 5 | [dependencies] 6 | rand ="0.5" -------------------------------------------------------------------------------- /diffie-hellman/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate rand; 2 | 3 | use rand::prelude::*; 4 | 5 | pub fn private_key(p: u64) -> u64 { 6 | let mut rng = thread_rng(); 7 | rng.gen_range(2, p) 8 | } 9 | 10 | pub fn public_key(p: u64, g: u64, a: u64) -> u64 { 11 | g.pow(a as u32) % p 12 | } 13 | 14 | pub fn secret(p: u64, b_pub: u64, a: u64) -> u64 { 15 | b_pub.pow(a as u32) % p 16 | } 17 | -------------------------------------------------------------------------------- /diffie-hellman/tests/diffie-hellman.rs: -------------------------------------------------------------------------------- 1 | extern crate diffie_hellman; 2 | 3 | use diffie_hellman::*; 4 | 5 | #[test] 6 | fn test_private_key_in_range_key() { 7 | let primes: Vec = vec![ 8 | 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 773, 967, 3461, 6131, 9 | ]; 10 | let private_keys: Vec = primes.iter().map(|x| private_key(*x)).collect(); 11 | 12 | for i in 0..primes.len() { 13 | assert!(1 < private_keys[i] && private_keys[i] < primes[i]); 14 | } 15 | } 16 | 17 | #[test] 18 | fn test_public_key_correct() { 19 | let p: u64 = 23; 20 | let g: u64 = 5; 21 | 22 | let private_key: u64 = 6; 23 | let expected: u64 = 8; 24 | 25 | assert_eq!(public_key(p, g, private_key), expected); 26 | } 27 | 28 | #[test] 29 | fn test_secret_key_correct() { 30 | let p: u64 = 11; 31 | 32 | let private_key_a = 7; 33 | let public_key_b = 8; 34 | let secret = secret(p, public_key_b, private_key_a); 35 | let expected = 2; 36 | 37 | assert_eq!(secret, expected); 38 | } 39 | 40 | #[test] 41 | fn test_changed_secret_key() { 42 | let p: u64 = 13; 43 | let g: u64 = 11; 44 | 45 | let private_key_a = private_key(p); 46 | let private_key_b = private_key(p); 47 | 48 | let public_key_a = public_key(p, g, private_key_a); 49 | let public_key_b = public_key(p, g, private_key_b); 50 | 51 | // Key exchange 52 | let secret_a = secret(p, public_key_b, private_key_a); 53 | let secret_b = secret(p, public_key_a, private_key_b); 54 | 55 | assert_eq!(secret_a, secret_b); 56 | } 57 | -------------------------------------------------------------------------------- /dominoes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dominoes" 3 | version = "2.1.0" 4 | -------------------------------------------------------------------------------- /dot-dsl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dot-dsl" 3 | version = "0.1.0" 4 | 5 | [dependencies] 6 | maplit = "1.0.1" -------------------------------------------------------------------------------- /etl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "etl" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /etl/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | pub fn transform(input: &BTreeMap>) -> BTreeMap { 4 | input 5 | .iter() 6 | .flat_map(|(&k, vec)| { 7 | vec.into_iter() 8 | .map(|v| (v.to_lowercase(), k)) 9 | .collect::>() 10 | }) 11 | .collect() 12 | } 13 | -------------------------------------------------------------------------------- /forth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "forth" 3 | version = "0.0.0" -------------------------------------------------------------------------------- /gigasecond/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gigasecond" 3 | version = "0.1.0" 4 | authors = ["Iain Diamond "] 5 | 6 | [dependencies] 7 | chrono = "0.3" 8 | 9 | -------------------------------------------------------------------------------- /gigasecond/README.md: -------------------------------------------------------------------------------- 1 | # Gigasecond 2 | 3 | Calculate the moment when someone has lived for 10^9 seconds. 4 | 5 | A gigasecond is 10^9 (1,000,000,000) seconds. 6 | 7 | ## Rust Installation 8 | 9 | Refer to the [exercism help page][help-page] for Rust installation and learning 10 | resources. 11 | 12 | ## Writing the Code 13 | 14 | Execute the tests with: 15 | 16 | ```bash 17 | $ cargo test 18 | ``` 19 | 20 | All but the first test have been ignored. After you get the first test to 21 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 22 | to pass again. The test file is located in the `tests` directory. You can 23 | also remove the ignore flag from all the tests to get them to run all at once 24 | if you wish. 25 | 26 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 27 | haven't already, it will help you with organizing your files. 28 | 29 | ## Feedback, Issues, Pull Requests 30 | 31 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 32 | 33 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 34 | 35 | [help-page]: http://exercism.io/languages/rust 36 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 37 | 38 | ## Source 39 | 40 | Chapter 9 in Chris Pine's online Learn to Program tutorial. [http://pine.fm/LearnToProgram/?Chapter=09](http://pine.fm/LearnToProgram/?Chapter=09) 41 | 42 | ## Submitting Incomplete Problems 43 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 44 | 45 | -------------------------------------------------------------------------------- /gigasecond/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate chrono; 2 | use chrono::*; 3 | 4 | pub fn after(start_date : DateTime) -> DateTime { 5 | start_date + Duration::seconds(1_000_000_000) 6 | } 7 | -------------------------------------------------------------------------------- /gigasecond/tests/gigasecond.rs: -------------------------------------------------------------------------------- 1 | extern crate gigasecond; 2 | 3 | /* 4 | * Students, 5 | * 6 | * Rust does not currently have a library for handling Time. To solve this exercise 7 | * you'll need to use the Chrono 'crate' (which is Rust's term for an external library). 8 | * 9 | * The first time you run `cargo test`, the Chrono crate will automatically be downloaded 10 | * and installed. More information on crates can be found at 11 | * https://doc.rust-lang.org/book/guessing-game.html#generating-a-secret-number 12 | * 13 | * In order to use the crate, your solution will need to start with the two following lines 14 | */ 15 | extern crate chrono; 16 | use chrono::*; 17 | 18 | #[test] 19 | fn test_date() { 20 | let start_date = UTC.ymd(2011, 4, 25).and_hms(0,0,0); 21 | assert_eq!(gigasecond::after(start_date), UTC.ymd(2043, 1, 1).and_hms(1,46,40)); 22 | } 23 | 24 | #[test] 25 | fn test_another_date() { 26 | let start_date = UTC.ymd(1977, 6, 13).and_hms(0,0,0); 27 | assert_eq!(gigasecond::after(start_date), UTC.ymd(2009, 2, 19).and_hms(1,46,40)); 28 | } 29 | 30 | #[test] 31 | fn test_third_date() { 32 | let start_date = UTC.ymd(1959, 7, 19).and_hms(0,0,0); 33 | assert_eq!(gigasecond::after(start_date), UTC.ymd(1991, 3, 27).and_hms(1,46,40)); 34 | } 35 | 36 | #[test] 37 | fn test_datetime() { 38 | let start_date = UTC.ymd(2015, 1, 24).and_hms(22,0,0); 39 | assert_eq!(gigasecond::after(start_date), UTC.ymd(2046, 10, 2).and_hms(23,46,40)); 40 | } 41 | 42 | #[test] 43 | fn test_another_datetime() { 44 | let start_date = UTC.ymd(2015, 1, 24).and_hms(23,59,59); 45 | assert_eq!(gigasecond::after(start_date), UTC.ymd(2046, 10, 3).and_hms(1,46,39)); 46 | } 47 | -------------------------------------------------------------------------------- /grade-school/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grade-school" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /grade-school/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | #[derive(Default)] 4 | pub struct School { 5 | students: HashMap>, 6 | } 7 | 8 | impl School { 9 | pub fn new() -> School { 10 | School { 11 | students: HashMap::new(), 12 | } 13 | } 14 | 15 | pub fn add(&mut self, grade: u32, student: &str) { 16 | let grade = self.students.entry(grade).or_insert_with(Vec::new); 17 | grade.push(student.into()); 18 | grade.sort(); 19 | } 20 | 21 | pub fn grades(&self) -> Vec { 22 | let mut grades: Vec = self.students.keys().cloned().collect(); 23 | grades.sort(); 24 | grades 25 | } 26 | 27 | pub fn grade(&self, grade: u32) -> Option> { 28 | self.students.get(&grade).cloned() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /grade-school/tests/grade-school.rs: -------------------------------------------------------------------------------- 1 | extern crate grade_school as school; 2 | 3 | fn some_strings(v: Vec<&str>) -> Option> { 4 | Some(v.iter().map(|s| s.to_string()).collect()) 5 | } 6 | 7 | #[test] 8 | fn test_grades_for_empty_school() { 9 | let s = school::School::new(); 10 | assert_eq!(s.grades(), vec![]); 11 | } 12 | 13 | #[test] 14 | fn test_grades_for_one_student() { 15 | let mut s = school::School::new(); 16 | s.add(2, "Aimee"); 17 | assert_eq!(s.grades(), vec![2]); 18 | } 19 | 20 | #[test] 21 | fn test_grades_for_several_students_are_sorted() { 22 | let mut s = school::School::new(); 23 | s.add(2, "Aimee"); 24 | s.add(7, "Logan"); 25 | s.add(4, "Blair"); 26 | assert_eq!(s.grades(), vec![2, 4, 7]); 27 | } 28 | 29 | #[test] 30 | fn test_grades_when_several_students_have_the_same_grade() { 31 | let mut s = school::School::new(); 32 | s.add(2, "Aimee"); 33 | s.add(2, "Logan"); 34 | s.add(2, "Blair"); 35 | assert_eq!(s.grades(), vec![2]); 36 | } 37 | 38 | #[test] 39 | fn test_grade_for_empty_school() { 40 | let s = school::School::new(); 41 | assert_eq!(s.grade(1), None); 42 | } 43 | 44 | #[test] 45 | fn test_grade_when_no_students_have_that_grade() { 46 | let mut s = school::School::new(); 47 | s.add(7, "Logan"); 48 | assert_eq!(s.grade(1), None); 49 | } 50 | 51 | #[test] 52 | fn test_grade_for_one_student() { 53 | let mut s = school::School::new(); 54 | s.add(2, "Aimee"); 55 | assert_eq!(s.grade(2), some_strings(vec!["Aimee"])); 56 | } 57 | 58 | #[test] 59 | fn test_grade_returns_students_sorted_by_name() { 60 | let mut s = school::School::new(); 61 | s.add(2, "James"); 62 | s.add(2, "Blair"); 63 | s.add(2, "Paul"); 64 | assert_eq!(s.grade(2), some_strings(vec!["Blair", "James", "Paul"])); 65 | } 66 | 67 | #[test] 68 | fn test_add_students_to_different_grades() { 69 | let mut s = school::School::new(); 70 | s.add(3, "Chelsea"); 71 | s.add(7, "Logan"); 72 | assert_eq!(s.grades(), vec![3, 7]); 73 | assert_eq!(s.grade(3), some_strings(vec!["Chelsea"])); 74 | assert_eq!(s.grade(7), some_strings(vec!["Logan"])); 75 | } 76 | -------------------------------------------------------------------------------- /grains/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grains" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /grains/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn square(s: u32) -> u64 { 2 | if s < 1 || s > 64 { 3 | panic!("Square must be between 1 and 64"); 4 | } 5 | 2u64.pow(s - 1) 6 | } 7 | 8 | pub fn total() -> u64 { 9 | let mut total = 0; 10 | for s in 1..=64 { 11 | total += square(s); 12 | } 13 | total 14 | } 15 | -------------------------------------------------------------------------------- /grains/tests/grains.rs: -------------------------------------------------------------------------------- 1 | extern crate grains; 2 | 3 | #[test] 4 | fn square_one() { 5 | assert_eq!(grains::square(1), 1); 6 | } 7 | 8 | #[test] 9 | fn square_two() { 10 | assert_eq!(grains::square(2), 2); 11 | } 12 | 13 | #[test] 14 | fn square_three() { 15 | assert_eq!(grains::square(3), 4); 16 | } 17 | 18 | #[test] 19 | fn square_four() { 20 | assert_eq!(grains::square(4), 8); 21 | } 22 | 23 | #[test] 24 | fn square_sixteen() { 25 | assert_eq!(grains::square(16), 32_768); 26 | } 27 | 28 | #[test] 29 | fn square_thirty_two() { 30 | assert_eq!(grains::square(32), 2_147_483_648); 31 | } 32 | 33 | #[test] 34 | fn square_sixty_four() { 35 | assert_eq!(grains::square(64), 9_223_372_036_854_775_808); 36 | } 37 | 38 | #[test] 39 | #[should_panic(expected = "Square must be between 1 and 64")] 40 | fn square_zero_panics() { 41 | grains::square(0); 42 | } 43 | 44 | #[test] 45 | #[should_panic(expected = "Square must be between 1 and 64")] 46 | fn square_sixty_five_panics() { 47 | grains::square(65); 48 | } 49 | 50 | #[test] 51 | fn total_sums_all_squares() { 52 | assert_eq!(grains::total(), 18_446_744_073_709_551_615); 53 | grains::total(); 54 | } 55 | -------------------------------------------------------------------------------- /grep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grep" 3 | version = "1.1.0" 4 | 5 | [dependencies] 6 | failure = "0.1.1" 7 | regex = "1" 8 | -------------------------------------------------------------------------------- /hamming/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hamming" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /hamming/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | pub fn hamming_distance(strand1: &str, strand2: &str) -> Result { 3 | if strand1.len() != strand2.len() { 4 | return Err("Unequal strand lengths!".into()); 5 | } 6 | 7 | Ok(strand1 8 | .chars() 9 | .zip(strand2.chars()) 10 | .filter(|&(x, y)| x != y) 11 | .count()) 12 | } 13 | -------------------------------------------------------------------------------- /hamming/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate hamming; 2 | 3 | fn main() { 4 | let result = hamming::hamming_distance("GGACG", "GGTCG").unwrap(); 5 | print!("{}", result); 6 | } 7 | -------------------------------------------------------------------------------- /hamming/tests/hamming.rs: -------------------------------------------------------------------------------- 1 | extern crate hamming; 2 | 3 | #[test] 4 | fn test_no_difference_between_empty_strands() { 5 | assert_eq!(hamming::hamming_distance("", "").unwrap(), 0); 6 | } 7 | 8 | #[test] 9 | fn test_no_difference_between_identical_strands() { 10 | assert_eq!(hamming::hamming_distance("GGACTGA", "GGACTGA").unwrap(), 0); 11 | } 12 | 13 | #[test] 14 | fn test_complete_hamming_distance_in_small_strand() { 15 | assert_eq!(hamming::hamming_distance("ACT", "GGA").unwrap(), 3); 16 | } 17 | 18 | #[test] 19 | fn test_small_hamming_distance_in_the_middle_somewhere() { 20 | assert_eq!(hamming::hamming_distance("GGACG", "GGTCG").unwrap(), 1); 21 | } 22 | 23 | #[test] 24 | fn test_larger_distance() { 25 | assert_eq!(hamming::hamming_distance("ACCAGGG", "ACTATGG").unwrap(), 2); 26 | } 27 | 28 | #[test] 29 | fn test_first_string_is_longer() { 30 | assert!(hamming::hamming_distance("AAA", "AA").is_err()); 31 | } 32 | 33 | #[test] 34 | fn test_second_string_is_longer() { 35 | assert!(hamming::hamming_distance("A", "AA").is_err()); 36 | } 37 | -------------------------------------------------------------------------------- /isbn-verifier/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "isbn-verifier" 3 | version = "1.1.0" 4 | -------------------------------------------------------------------------------- /isbn-verifier/src/lib.rs: -------------------------------------------------------------------------------- 1 | /// Determines whether the supplied string is a valid ISBN number 2 | pub fn is_valid_isbn(isbn: &str) -> bool { 3 | let reversed: String = isbn.chars().rev().collect(); 4 | let (head, tail) = reversed.split_at(1); 5 | let check_digit = match head { 6 | "X" => 10, 7 | digit => match digit.parse::() { 8 | Ok(value) => value, 9 | _ => return false, 10 | }, 11 | }; 12 | let mut count = 2; 13 | let mut total = check_digit; 14 | 15 | let filtered = tail.to_string().replace("-", ""); 16 | 17 | for character in filtered.chars() { 18 | match character.to_digit(10) { 19 | Some(value) => { 20 | total += count * value; 21 | count += 1; 22 | } 23 | _ => return false, 24 | } 25 | } 26 | total % 11 == 0 27 | } 28 | -------------------------------------------------------------------------------- /isbn-verifier/tests/isbn-verifier.rs: -------------------------------------------------------------------------------- 1 | extern crate isbn_verifier; 2 | 3 | use isbn_verifier::is_valid_isbn; 4 | 5 | #[test] 6 | fn test_valid() { 7 | assert!(is_valid_isbn("3-598-21508-8")); 8 | } 9 | 10 | #[test] 11 | fn test_invalid_check_digit() { 12 | assert!(!is_valid_isbn("3-598-21508-9")); 13 | } 14 | 15 | #[test] 16 | fn test_valid_check_digit_of_10() { 17 | assert!(is_valid_isbn("3-598-21507-X")); 18 | } 19 | 20 | #[test] 21 | fn test_invalid_character_as_check_digit() { 22 | assert!(!is_valid_isbn("3-598-21507-A")); 23 | } 24 | 25 | #[test] 26 | fn test_invalid_character_in_isbn() { 27 | assert!(!is_valid_isbn("3-598-2K507-0")); 28 | } 29 | 30 | #[test] 31 | #[allow(non_snake_case)] 32 | fn test_invalid_isbn_with_invalid_X() { 33 | assert!(!is_valid_isbn("3-598-2X507-9")); 34 | } 35 | 36 | #[test] 37 | fn test_valid_isbn_without_dashes() { 38 | assert!(is_valid_isbn("3598215088")); 39 | } 40 | 41 | #[test] 42 | #[allow(non_snake_case)] 43 | fn test_valid_isbn_without_dashes_and_X_as_check() { 44 | assert!(is_valid_isbn("359821507X")); 45 | } 46 | 47 | #[test] 48 | fn test_invalid_isbn_without_dashes_and_no_check_digit() { 49 | assert!(!is_valid_isbn("359821507")); 50 | } 51 | 52 | #[test] 53 | fn test_invalid_isbn_without_dashes_and_too_long() { 54 | assert!(!is_valid_isbn("3598215078X")); 55 | } 56 | 57 | #[test] 58 | fn test_invalid_isbn_without_check_digit() { 59 | assert!(!is_valid_isbn("3-598-21507")); 60 | } 61 | 62 | #[test] 63 | fn test_invalid_isbn_too_long() { 64 | assert!(!is_valid_isbn("3-598-21507-XX")); 65 | } 66 | 67 | #[test] 68 | #[allow(non_snake_case)] 69 | fn test_invalid_isbn_with_check_digit_X_instead_of_0() { 70 | assert!(!is_valid_isbn("3-598-21515-X")); 71 | } 72 | -------------------------------------------------------------------------------- /isogram/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | **/*.rs.bk 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 8 | Cargo.lock 9 | -------------------------------------------------------------------------------- /isogram/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "isogram" 3 | version = "1.3.0" 4 | -------------------------------------------------------------------------------- /isogram/README.md: -------------------------------------------------------------------------------- 1 | # Isogram 2 | 3 | Determine if a word or phrase is an isogram. 4 | 5 | An isogram (also known as a "nonpattern word") is a word or phrase without a repeating letter, however spaces and hyphens are allowed to appear multiple times. 6 | 7 | Examples of isograms: 8 | 9 | - lumberjacks 10 | - background 11 | - downstream 12 | - six-year-old 13 | 14 | The word *isograms*, however, is not an isogram, because the s repeats. 15 | 16 | ## Rust Installation 17 | 18 | Refer to the [exercism help page][help-page] for Rust installation and learning 19 | resources. 20 | 21 | ## Writing the Code 22 | 23 | Execute the tests with: 24 | 25 | ```bash 26 | $ cargo test 27 | ``` 28 | 29 | All but the first test have been ignored. After you get the first test to 30 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 31 | to pass again. The test file is located in the `tests` directory. You can 32 | also remove the ignore flag from all the tests to get them to run all at once 33 | if you wish. 34 | 35 | Make sure to read the [Modules](https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html) chapter if you 36 | haven't already, it will help you with organizing your files. 37 | 38 | ## Feedback, Issues, Pull Requests 39 | 40 | The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help! 41 | 42 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md). 43 | 44 | [help-page]: http://exercism.io/languages/rust 45 | [modules]: https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html 46 | [cargo]: https://doc.rust-lang.org/book/second-edition/ch14-00-more-about-cargo.html 47 | 48 | ## Source 49 | 50 | Wikipedia [https://en.wikipedia.org/wiki/Isogram](https://en.wikipedia.org/wiki/Isogram) 51 | 52 | ## Submitting Incomplete Solutions 53 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 54 | -------------------------------------------------------------------------------- /isogram/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn check(candidate: &str) -> bool { 2 | let mut letters: Vec = candidate 3 | .to_lowercase() 4 | .chars() 5 | .filter(|char| char.is_alphabetic()) 6 | .collect(); 7 | while let Some(letter) = letters.pop() { 8 | if letters.contains(&letter) { 9 | return false; 10 | } 11 | } 12 | true 13 | } 14 | -------------------------------------------------------------------------------- /isogram/tests/isogram.rs: -------------------------------------------------------------------------------- 1 | extern crate isogram; 2 | 3 | use isogram::check; 4 | 5 | #[test] 6 | fn empty_string() { 7 | assert_eq!(check(""), true, "An empty string should be an isogram.") 8 | } 9 | 10 | #[test] 11 | fn only_lower_case_characters() { 12 | assert_eq!(check("isogram"), true, "\"isogram\" should be an isogram.") 13 | } 14 | 15 | #[test] 16 | fn one_duplicated_character() { 17 | assert_eq!( 18 | check("eleven"), 19 | false, 20 | "\"eleven\" has more than one \'e\', therefore it is no isogram." 21 | ) 22 | } 23 | 24 | #[test] 25 | fn longest_reported_english_isogram() { 26 | assert_eq!( 27 | check("subdermatoglyphic"), 28 | true, 29 | "\"subdermatoglyphic\" should be an isogram." 30 | ) 31 | } 32 | 33 | #[test] 34 | fn one_duplicated_character_mixed_case() { 35 | assert_eq!( 36 | check("Alphabet"), 37 | false, 38 | "\"Alphabet\" has more than one \'a\', therefore it is no isogram." 39 | ) 40 | } 41 | 42 | #[test] 43 | fn hypothetical_isogramic_word_with_hyphen() { 44 | assert_eq!( 45 | check("thumbscrew-japingly"), 46 | true, 47 | "\"thumbscrew-japingly\" should be an isogram." 48 | ) 49 | } 50 | 51 | #[test] 52 | fn isogram_with_duplicated_hyphen() { 53 | assert_eq!( 54 | check("six-year-old"), 55 | true, 56 | "\"six-year-old\" should be an isogram." 57 | ) 58 | } 59 | 60 | #[test] 61 | fn made_up_name_that_is_an_isogram() { 62 | assert_eq!( 63 | check("Emily Jung Schwartzkopf"), 64 | true, 65 | "\"Emily Jung Schwartzkopf\" should be an isogram." 66 | ) 67 | } 68 | 69 | #[test] 70 | fn duplicated_character_in_the_middle() { 71 | assert_eq!( 72 | check("accentor"), 73 | false, 74 | "\"accentor\" has more than one \'c\', therefore it is no isogram." 75 | ) 76 | } 77 | -------------------------------------------------------------------------------- /largest-series-product/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "largest-series-product" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /largest-series-product/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn lsp(sequence: &str, size: usize) -> Result { 2 | if sequence.len() < size || !sequence.chars().all(char::is_numeric) { 3 | return Err("Invalid parameters."); 4 | } else if size == 0 { 5 | return Ok(1); 6 | } 7 | 8 | let digits: Vec = sequence 9 | .chars() 10 | .map(|ch| ch.to_digit(10).unwrap()) 11 | .collect(); 12 | 13 | Ok(digits 14 | .windows(size) 15 | .map(|window| window.iter().product()) 16 | .max() 17 | .unwrap()) 18 | } 19 | -------------------------------------------------------------------------------- /leap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "leap" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /leap/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn is_leap_year(year: u16) -> bool { 2 | year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) 3 | } 4 | -------------------------------------------------------------------------------- /leap/tests/leap.rs: -------------------------------------------------------------------------------- 1 | extern crate leap; 2 | 3 | #[test] 4 | fn test_vanilla_leap_year() { 5 | assert_eq!(leap::is_leap_year(1996), true); 6 | } 7 | 8 | #[test] 9 | fn test_any_old_year() { 10 | assert_eq!(leap::is_leap_year(1997), false); 11 | } 12 | 13 | #[test] 14 | fn test_century() { 15 | assert_eq!(leap::is_leap_year(1900), false); 16 | } 17 | 18 | #[test] 19 | fn test_exceptional_centuries() { 20 | assert_eq!(leap::is_leap_year(2000), true); 21 | assert_eq!(leap::is_leap_year(2400), true); 22 | } 23 | -------------------------------------------------------------------------------- /luhn-from/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "luhn-from" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /luhn-from/README.md: -------------------------------------------------------------------------------- 1 | # Luhn From 2 | 3 | Luhn: Using the From Trait 4 | 5 | # Luhn: Using the From Trait 6 | 7 | Before doing this exercise you should probably do the original Luhn exercise. If you have not completed Luhn, you can get it by running the command: 8 | 9 | > `exercism fetch rust luhn` 10 | 11 | In the original Luhn exercise you only validated strings, but the Luhn algorithm can be applied to integers as well. 12 | 13 | In this exercise you'll implement the [From trait](https://doc.rust-lang.org/std/convert/trait.From.html) to convert strings, strs and unsigned integers into a Struct that performs the validation. 14 | 15 | ## Rust Installation 16 | 17 | Refer to the [exercism help page][help-page] for Rust installation and learning 18 | resources. 19 | 20 | ## Writing the Code 21 | 22 | Execute the tests with: 23 | 24 | ```bash 25 | $ cargo test 26 | ``` 27 | 28 | All but the first test have been ignored. After you get the first test to 29 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 30 | to pass again. The test file is located in the `tests` directory. You can 31 | also remove the ignore flag from all the tests to get them to run all at once 32 | if you wish. 33 | 34 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 35 | haven't already, it will help you with organizing your files. 36 | 37 | ## Feedback, Issues, Pull Requests 38 | 39 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 40 | 41 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 42 | 43 | [help-page]: http://exercism.io/languages/rust 44 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 45 | 46 | ## Source 47 | 48 | The Rust track maintainers, based on the original Luhn exercise 49 | 50 | ## Submitting Incomplete Problems 51 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 52 | 53 | -------------------------------------------------------------------------------- /luhn-from/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct Luhn { 3 | text: String, 4 | } 5 | 6 | impl From for Luhn { 7 | fn from(input: T) -> Self { 8 | Luhn { 9 | text: input.to_string(), 10 | } 11 | } 12 | } 13 | 14 | impl Luhn { 15 | pub fn is_valid(&self) -> bool { 16 | let cleaned = self.text.replace(" ", ""); 17 | let is_valid_sequence = |seq: &str| cleaned.len() > 1 && seq.chars().all(char::is_numeric); 18 | 19 | if is_valid_sequence(&cleaned) { 20 | return Self::calculate_sum(&cleaned) % 10 == 0; 21 | } 22 | false 23 | } 24 | 25 | fn calculate_sum(sequence: &str) -> u32 { 26 | let is_odd = |x| x % 2 != 0; 27 | 28 | sequence 29 | .chars() 30 | .rev() 31 | .enumerate() 32 | .map(|(index, chr)| { 33 | let mut value = chr.to_digit(10).unwrap(); 34 | 35 | if is_odd(index) { 36 | value *= 2; 37 | if value > 9 { 38 | value -= 9; 39 | } 40 | } 41 | value 42 | }) 43 | .sum() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /luhn-trait/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock -------------------------------------------------------------------------------- /luhn-trait/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "luhn-trait" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /luhn-trait/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub trait ValidLuhn { 2 | fn valid_luhn(&self) -> bool; 3 | fn calculate_sum(sequence: &str) -> u32 { 4 | let is_odd = |x| x % 2 != 0; 5 | 6 | sequence 7 | .chars() 8 | .rev() 9 | .enumerate() 10 | .map(|(index, chr)| { 11 | let mut value = chr.to_digit(10).unwrap(); 12 | 13 | if is_odd(index) { 14 | value *= 2; 15 | if value > 9 { 16 | value -= 9; 17 | } 18 | } 19 | value 20 | }) 21 | .sum() 22 | } 23 | fn is_valid_sequence(seq: &str) -> bool { 24 | seq.len() > 1 && seq.chars().all(char::is_numeric) 25 | } 26 | } 27 | 28 | impl ValidLuhn for T { 29 | fn valid_luhn(&self) -> bool { 30 | let cleaned: String = self.to_string().replace(" ", ""); 31 | if Self::is_valid_sequence(&cleaned) { 32 | return Self::calculate_sum(&cleaned) % 10 == 0; 33 | } 34 | false 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /luhn-trait/tests/luhn-trait.rs: -------------------------------------------------------------------------------- 1 | extern crate luhn_trait; 2 | 3 | use luhn_trait::*; 4 | 5 | #[test] 6 | fn you_can_validate_from_a_str() { 7 | assert!("046 454 286".valid_luhn()); 8 | assert!(!"046 454 287".valid_luhn()); 9 | } 10 | 11 | #[test] 12 | fn you_can_validate_from_a_string() { 13 | assert!(String::from("046 454 286").valid_luhn()); 14 | assert!(!String::from("046 454 287").valid_luhn()); 15 | } 16 | 17 | #[test] 18 | fn you_can_validate_from_a_u8() { 19 | assert!(240u8.valid_luhn()); 20 | assert!(!241u8.valid_luhn()); 21 | } 22 | 23 | #[test] 24 | fn you_can_validate_from_a_u16() { 25 | let valid = 64_436u16; 26 | let invalid = 64_437u16; 27 | assert!(valid.valid_luhn()); 28 | assert!(!invalid.valid_luhn()); 29 | } 30 | 31 | #[test] 32 | fn you_can_validate_from_a_u32() { 33 | let valid = 46_454_286u32; 34 | let invalid = 46_454_287u32; 35 | assert!(valid.valid_luhn()); 36 | assert!(!invalid.valid_luhn()); 37 | } 38 | 39 | #[test] 40 | fn you_can_validate_from_a_u64() { 41 | let valid = 8273_1232_7352_0562u64; 42 | let invalid = 8273_1232_7352_0569u64; 43 | assert!(valid.valid_luhn()); 44 | assert!(!invalid.valid_luhn()); 45 | } 46 | 47 | #[test] 48 | fn you_can_validate_from_a_usize() { 49 | let valid = 8273_1232_7352_0562usize; 50 | let invalid = 8273_1232_7352_0569usize; 51 | assert!(valid.valid_luhn()); 52 | assert!(!invalid.valid_luhn()); 53 | } 54 | -------------------------------------------------------------------------------- /luhn/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "luhn" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /luhn/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn is_valid(sequence: &str) -> bool { 2 | let cleaned = sequence.replace(" ", ""); 3 | if cleaned.len() > 1 && is_valid_sequence(&cleaned) { 4 | return calculate_sum(&cleaned) % 10 == 0; 5 | } 6 | false 7 | } 8 | 9 | fn calculate_sum(sequence: &str) -> u32 { 10 | sequence 11 | .chars() 12 | .rev() 13 | .enumerate() 14 | .map(|(index, chr)| { 15 | let mut value = chr.to_digit(10).unwrap(); 16 | 17 | if is_odd(index) { 18 | value *= 2; 19 | if value > 9 { 20 | value -= 9; 21 | } 22 | } 23 | value 24 | }) 25 | .sum::() 26 | } 27 | 28 | fn is_valid_sequence(sequence: &str) -> bool { 29 | sequence.chars().all(char::is_numeric) 30 | } 31 | 32 | fn is_odd(x: usize) -> bool { 33 | x % 2 != 0 34 | } 35 | -------------------------------------------------------------------------------- /luhn/tests/luhn.rs: -------------------------------------------------------------------------------- 1 | extern crate luhn; 2 | 3 | use luhn::*; 4 | 5 | #[test] 6 | fn single_digit_string_is_invalid() { 7 | assert!(!is_valid("1")); 8 | } 9 | 10 | #[test] 11 | fn single_zero_string_is_invalid() { 12 | assert!(!is_valid("0")); 13 | } 14 | 15 | #[test] 16 | fn simple_valid_sin() { 17 | assert!(is_valid(" 5 9 ")); 18 | } 19 | 20 | #[test] 21 | fn valid_canadian_sin_is_valid() { 22 | assert!(is_valid("046 454 286")); 23 | } 24 | 25 | #[test] 26 | fn invalid_canadian_sin_is_invalid() { 27 | assert!(!is_valid("046 454 287")); 28 | } 29 | 30 | #[test] 31 | fn invalid_credit_card_is_invalid() { 32 | assert!(!is_valid("8273 1232 7352 0569")); 33 | } 34 | 35 | #[test] 36 | fn strings_that_contain_non_digits_are_invalid() { 37 | assert!(!is_valid("046a 454 286")); 38 | } 39 | 40 | #[test] 41 | fn punctuation_is_invalid() { 42 | assert!(!is_valid("055-444-285")); 43 | } 44 | 45 | #[test] 46 | fn symbols_are_invalid() { 47 | assert!(!is_valid("055£ 444$ 285")); 48 | } 49 | 50 | #[test] 51 | fn single_digit_with_space_is_invalid() { 52 | assert!(!is_valid(" 0")); 53 | } 54 | 55 | #[test] 56 | fn lots_of_zeros_are_valid() { 57 | assert!(is_valid(" 00000")); 58 | } 59 | 60 | #[test] 61 | fn another_valid_sin() { 62 | assert!(is_valid("055 444 285")); 63 | } 64 | 65 | #[test] 66 | fn nine_doubled_is_nine() { 67 | assert!(is_valid("091")); 68 | } 69 | -------------------------------------------------------------------------------- /macros/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Cargo.lock if creating a library 2 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "macros" 3 | version = "0.1.0" 4 | authors = ["Peter Goodspeed-Niklaus "] 5 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(macro_at_most_once_rep)] 2 | 3 | #[macro_export] 4 | macro_rules! hashmap { 5 | () => { 6 | HashMap::new() 7 | }; 8 | 9 | ( $($key:expr => $value:expr),* $(,)?) => { 10 | { 11 | let mut hm = HashMap::new(); 12 | $( 13 | hm.insert($key, $value); 14 | )* 15 | hm 16 | } 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /macros/tests/macros.rs: -------------------------------------------------------------------------------- 1 | #![feature(macro_at_most_once_rep)] 2 | 3 | #[macro_use] 4 | extern crate macros; 5 | 6 | use std::collections::HashMap; 7 | 8 | #[test] 9 | fn test_empty() { 10 | let expected: HashMap = HashMap::new(); 11 | let computed: HashMap = hashmap!(); 12 | assert_eq!(computed, expected); 13 | } 14 | 15 | #[test] 16 | fn test_no_trailing_comma() { 17 | let mut expected = HashMap::new(); 18 | expected.insert(1, "one"); 19 | expected.insert(2, "two"); 20 | assert_eq!(hashmap!(1 => "one", 2 => "two"), expected); 21 | } 22 | 23 | #[test] 24 | fn test_trailing_comma() { 25 | let mut expected = HashMap::new(); 26 | expected.insert('h', 89); 27 | expected.insert('a', 1); 28 | expected.insert('s', 19); 29 | expected.insert('h', 8); 30 | assert_eq!( 31 | hashmap!( 32 | 'h' => 89, 33 | 'a' => 1, 34 | 's' => 19, 35 | 'h' => 8, 36 | ), 37 | expected 38 | ); 39 | } 40 | 41 | #[test] 42 | fn test_nested() { 43 | let mut expected = HashMap::new(); 44 | expected.insert("non-empty", { 45 | let mut subhashmap = HashMap::new(); 46 | subhashmap.insert(23, 623); 47 | subhashmap.insert(34, 21); 48 | subhashmap 49 | }); 50 | expected.insert("empty", HashMap::new()); 51 | assert_eq!( 52 | hashmap!( 53 | "non-empty" => hashmap!( 54 | 23 => 623, 55 | 34 => 21 56 | ), 57 | "empty" => hashmap!() 58 | ), 59 | expected 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /minesweeper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minesweeper" 3 | version = "1.1.0" -------------------------------------------------------------------------------- /minesweeper/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::char; 2 | 3 | const ADJACENCIES: [(i8, i8); 8] = [ 4 | (-1, -1), 5 | (-1, 0), 6 | (-1, 1), 7 | (0, -1), 8 | (0, 1), 9 | (1, -1), 10 | (1, 0), 11 | (1, 1), 12 | ]; 13 | 14 | pub fn annotate(input: &[&str]) -> Vec { 15 | if input.is_empty() { 16 | return vec![]; 17 | } 18 | let board = Board::new(input); 19 | board 20 | .data 21 | .iter() 22 | .enumerate() 23 | .map(|(pos_0, row)| { 24 | row 25 | .iter() 26 | .enumerate() 27 | .map(|(pos_1, elem)| { 28 | if *elem != '*' { 29 | let count = board.neighbours((pos_0 as u8, pos_1 as u8)); 30 | if count > 0 { 31 | char::from_digit(u32::from(count), 10).unwrap() 32 | } else { 33 | ' ' 34 | } 35 | } else { 36 | *elem 37 | } 38 | }) 39 | .collect::() 40 | }) 41 | .collect() 42 | } 43 | 44 | #[derive(Debug)] 45 | struct Board { 46 | width: u8, 47 | height: u8, 48 | data: Vec>, 49 | } 50 | 51 | impl Board { 52 | fn new(input: &[&str]) -> Self { 53 | let data = input 54 | .into_iter() 55 | .map(|row| row.chars().collect::>()) 56 | .collect::>(); 57 | let height = data.len() as u8; 58 | let width = data[0].len() as u8; 59 | Board { 60 | width, 61 | height, 62 | data, 63 | } 64 | } 65 | fn neighbours(&self, position: (u8, u8)) -> u8 { 66 | let position = (position.0 as i8, position.1 as i8); 67 | ADJACENCIES 68 | .iter() 69 | .map(|adjacent| (position.0 + adjacent.0, position.1 + adjacent.1)) 70 | .filter(|neighbour| { 71 | neighbour.0 >= 0 72 | && neighbour.1 >= 0 73 | && neighbour.0 < self.height as i8 74 | && neighbour.1 < self.width as i8 75 | }) 76 | .fold(0, |acc, pos| { 77 | if self.data[pos.0 as usize][pos.1 as usize] == '*' { 78 | acc + 1 79 | } else { 80 | acc 81 | } 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /minesweeper/tests/minesweeper.rs: -------------------------------------------------------------------------------- 1 | extern crate minesweeper; 2 | 3 | use minesweeper::annotate; 4 | 5 | fn remove_annotations(board: &[&str]) -> Vec { 6 | board.iter().map(|r| remove_annotations_in_row(r)).collect() 7 | } 8 | 9 | fn remove_annotations_in_row(row: &str) -> String { 10 | row 11 | .chars() 12 | .map(|ch| match ch { 13 | '*' => '*', 14 | _ => ' ', 15 | }) 16 | .collect() 17 | } 18 | 19 | fn run_test(test_case: &[&str]) { 20 | let cleaned = remove_annotations(test_case); 21 | let cleaned_strs = cleaned.iter().map(|r| &r[..]).collect::>(); 22 | let expected = test_case.iter().map(|&r| r.to_string()).collect::>(); 23 | assert_eq!(expected, annotate(&cleaned_strs)); 24 | } 25 | 26 | #[test] 27 | fn no_rows() { 28 | run_test(&[]); 29 | } 30 | 31 | #[test] 32 | fn no_columns() { 33 | run_test(&[""]); 34 | } 35 | 36 | #[test] 37 | fn no_mines() { 38 | run_test(&[" ", " ", " "]); 39 | } 40 | 41 | #[test] 42 | fn board_with_only_mines() { 43 | run_test(&["***", "***", "***"]); 44 | } 45 | 46 | #[test] 47 | fn mine_surrounded_by_spaces() { 48 | run_test(&["111", "1*1", "111"]); 49 | } 50 | 51 | #[test] 52 | fn space_surrounded_by_mines() { 53 | run_test(&["***", "*8*", "***"]); 54 | } 55 | 56 | #[test] 57 | fn horizontal_line() { 58 | run_test(&["1*2*1"]); 59 | } 60 | 61 | #[test] 62 | fn horizontal_line_mines_at_edges() { 63 | run_test(&["*1 1*"]); 64 | } 65 | 66 | #[test] 67 | fn vertical_line() { 68 | run_test(&["1", "*", "2", "*", "1"]); 69 | } 70 | 71 | #[test] 72 | fn vertical_line_mines_at_edges() { 73 | run_test(&["*", "1", " ", "1", "*"]); 74 | } 75 | 76 | #[test] 77 | fn cross() { 78 | run_test(&[" 2*2 ", "25*52", "*****", "25*52", " 2*2 "]); 79 | } 80 | 81 | #[test] 82 | fn large_board() { 83 | run_test(&["1*22*1", "12*322", " 123*2", "112*4*", "1*22*2", "111111"]); 84 | } 85 | -------------------------------------------------------------------------------- /nth-prime/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /nth-prime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nth_prime" 3 | version = "1.0.0" 4 | authors = ["sacherjj "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /nth-prime/README.md: -------------------------------------------------------------------------------- 1 | # Nth Prime 2 | 3 | Given a number n, determine what the nth prime is. 4 | 5 | By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that 6 | the 6th prime is 13. 7 | 8 | If your language provides methods in the standard library to deal with prime 9 | numbers, pretend they don't exist and implement them yourself. 10 | 11 | ## Rust Installation 12 | 13 | Refer to the [exercism help page][help-page] for Rust installation and learning 14 | resources. 15 | 16 | ## Writing the Code 17 | 18 | Execute the tests with: 19 | 20 | ```bash 21 | $ cargo test 22 | ``` 23 | 24 | All but the first test have been ignored. After you get the first test to 25 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 26 | to pass again. The test file is located in the `tests` directory. You can 27 | also remove the ignore flag from all the tests to get them to run all at once 28 | if you wish. 29 | 30 | Make sure to read the [Modules](https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html) chapter if you 31 | haven't already, it will help you with organizing your files. 32 | 33 | ## Feedback, Issues, Pull Requests 34 | 35 | The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help! 36 | 37 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md). 38 | 39 | [help-page]: http://exercism.io/languages/rust 40 | [modules]: https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html 41 | [cargo]: https://doc.rust-lang.org/book/second-edition/ch14-00-more-about-cargo.html 42 | 43 | ## Source 44 | 45 | A variation on Problem 7 at Project Euler [http://projecteuler.net/problem=7](http://projecteuler.net/problem=7) 46 | 47 | ## Submitting Incomplete Solutions 48 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 49 | -------------------------------------------------------------------------------- /nth-prime/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn nth(n: usize) -> Option { 2 | match n { 3 | 0 => None, 4 | _ => (1..).filter(|&x| is_prime(x)).nth(n - 1), 5 | } 6 | } 7 | 8 | pub fn is_prime(n: u32) -> bool { 9 | if n <= 1 { 10 | return false; 11 | } 12 | let mut range = (2..=n).take_while(|x| x * x <= n); 13 | !range.any(|x| n % x == 0) 14 | } 15 | -------------------------------------------------------------------------------- /nth-prime/tests/nth-prime.rs: -------------------------------------------------------------------------------- 1 | extern crate nth_prime as np; 2 | 3 | #[test] 4 | fn test_first_prime() { 5 | assert_eq!(np::nth(1), Some(2)); 6 | } 7 | 8 | #[test] 9 | fn test_second_prime() { 10 | assert_eq!(np::nth(2), Some(3)); 11 | } 12 | 13 | #[test] 14 | fn test_sixth_prime() { 15 | assert_eq!(np::nth(6), Some(13)); 16 | } 17 | 18 | #[test] 19 | fn test_big_prime() { 20 | assert_eq!(np::nth(10001), Some(104743)); 21 | } 22 | 23 | #[test] 24 | fn test_zeroth_prime() { 25 | assert_eq!(np::nth(0), None); 26 | } 27 | -------------------------------------------------------------------------------- /nucleotide-count/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nucleotide-count" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /nucleotide-count/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | pub fn count(nucleo: char, sequence: &str) -> Result { 4 | if !is_valid(nucleo) || !is_valid_sequence(sequence) { 5 | return Err("Invalid Symbol found!"); 6 | } 7 | Ok(sequence.chars().filter(|&s| s == nucleo).count()) 8 | } 9 | 10 | pub fn nucleotide_counts(sequence: &str) -> Result, &str> { 11 | let mut letters = HashMap::new(); 12 | letters.insert('A', count('A', sequence)?); 13 | letters.insert('C', count('C', sequence)?); 14 | letters.insert('G', count('G', sequence)?); 15 | letters.insert('T', count('T', sequence)?); 16 | Ok(letters) 17 | } 18 | 19 | fn is_valid_sequence(sequence: &str) -> bool { 20 | sequence.chars().all(is_valid) 21 | } 22 | 23 | fn is_valid(nucleo: char) -> bool { 24 | "ACGT".contains(nucleo) 25 | } 26 | -------------------------------------------------------------------------------- /nucleotide-count/tests/nucleotide-count.rs: -------------------------------------------------------------------------------- 1 | extern crate nucleotide_count as dna; 2 | 3 | use std::collections::HashMap; 4 | 5 | fn check_dna(s: &str, pairs: &[(char, usize)]) { 6 | // The reason for the awkward code in here is to ensure that the failure 7 | // message for assert_eq! is as informative as possible. A simpler 8 | // solution would simply check the length of the map, and then 9 | // check for the presence and value of each key in the given pairs vector. 10 | let mut m: HashMap = dna::nucleotide_counts(s).unwrap(); 11 | for &(k, v) in pairs.iter() { 12 | assert_eq!((k, m.remove(&k).unwrap()), (k, v)); 13 | } 14 | // may fail with a message that clearly shows all extra pairs in the map 15 | assert_eq!(m.iter().collect::>(), vec![]); 16 | } 17 | 18 | #[test] 19 | fn count_returns_result() { 20 | assert!(dna::count('A', "").is_ok()); 21 | } 22 | 23 | #[test] 24 | fn test_count_empty() { 25 | assert_eq!(dna::count('A', "").unwrap(), 0); 26 | } 27 | 28 | #[test] 29 | fn count_invalid_nucleotide() { 30 | assert!(dna::count('X', "A").is_err()); 31 | } 32 | 33 | #[test] 34 | fn count_invalid_dna() { 35 | assert!(dna::count('A', "AX").is_err()); 36 | } 37 | 38 | #[test] 39 | fn test_count_repetitive_cytosine() { 40 | assert_eq!(dna::count('C', "CCCCC").unwrap(), 5); 41 | } 42 | 43 | #[test] 44 | fn test_count_only_thymine() { 45 | assert_eq!(dna::count('T', "GGGGGTAACCCGG").unwrap(), 1); 46 | } 47 | 48 | #[test] 49 | fn counts_returns_result() { 50 | assert!(dna::nucleotide_counts("ACGT").is_ok()); 51 | } 52 | 53 | #[test] 54 | fn test_nucleotide_count_empty() { 55 | check_dna("", &[('A', 0), ('T', 0), ('C', 0), ('G', 0)]); 56 | } 57 | 58 | #[test] 59 | fn test_nucleotide_count_only_guanine() { 60 | check_dna("GGGGGGGG", &[('A', 0), ('T', 0), ('C', 0), ('G', 8)]); 61 | } 62 | 63 | #[test] 64 | fn test_nucleotide_count_counts_all() { 65 | check_dna("AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAA\ 66 | GAGTGTCTGATAGCAGC", 67 | &[('A', 20), ('T', 21), ('C', 12), ('G', 17)]); 68 | } 69 | 70 | #[test] 71 | fn counts_invalid_nucleotide_results_in_err() { 72 | assert!(dna::nucleotide_counts("GGXXX").is_err()); 73 | } 74 | -------------------------------------------------------------------------------- /ocr-numbers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ocr-numbers" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /palindrome-products/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "palindrome-products" 3 | version = "1.1.0" 4 | authors = ["Kirill Meng "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /palindrome-products/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub type Palindrome = u64; 2 | 3 | fn is_palindrome(num: u64) -> bool { 4 | let mut number = num; 5 | let mut rev = 0; 6 | while number > 0 { 7 | let digit = number % 10; 8 | rev = rev * 10 + digit; 9 | number /= 10; 10 | } 11 | 12 | num == rev 13 | } 14 | 15 | fn get_products(start: Palindrome, end: Palindrome) -> Vec { 16 | let mut products = Vec::new(); 17 | for a in start..=end { 18 | for b in a..=end { 19 | let product = a * b; 20 | if is_palindrome(product) && !products.contains(&product) { 21 | products.push(product); 22 | } 23 | } 24 | } 25 | products 26 | } 27 | 28 | pub fn get_palindrome_products(min: Palindrome, max: Palindrome) -> Vec { 29 | let mut products = get_products(min, max); 30 | products.sort(); 31 | products 32 | } 33 | 34 | pub fn min(palindromes: &[Palindrome]) -> Option { 35 | if palindromes.is_empty() { 36 | return None; 37 | } 38 | palindromes.first().and_then(|value| Some(*value)) 39 | } 40 | 41 | pub fn max(palindromes: &[Palindrome]) -> Option { 42 | if palindromes.is_empty() { 43 | return None; 44 | } 45 | palindromes.last().and_then(|value| Some(*value)) 46 | } 47 | -------------------------------------------------------------------------------- /palindrome-products/tests/palindrome-products.rs: -------------------------------------------------------------------------------- 1 | extern crate palindrome_products; 2 | use palindrome_products::*; 3 | 4 | #[test] 5 | fn single_digits() { 6 | let palindromes = get_palindrome_products(1, 9); 7 | assert_eq!(min(&palindromes), Some(1)); 8 | assert_eq!(max(&palindromes), Some(9)); 9 | } 10 | 11 | #[test] 12 | fn double_digits() { 13 | let palindromes = get_palindrome_products(10, 99); 14 | assert_eq!(min(&palindromes), Some(121)); 15 | assert_eq!(max(&palindromes), Some(9009)); 16 | } 17 | 18 | #[test] 19 | fn triple_digits() { 20 | let palindromes = get_palindrome_products(100, 999); 21 | assert_eq!(min(&palindromes), Some(10201)); 22 | assert_eq!(max(&palindromes), Some(906609)); 23 | } 24 | 25 | #[test] 26 | fn four_digits() { 27 | let palindromes = get_palindrome_products(1000, 9999); 28 | assert_eq!(min(&palindromes), Some(1002001)); 29 | assert_eq!(max(&palindromes), Some(99000099)); 30 | } 31 | 32 | #[test] 33 | fn empty_result_for_smallest_palindrome() { 34 | assert_eq!(min(&get_palindrome_products(1002, 1003)), None); 35 | } 36 | 37 | #[test] 38 | fn empty_result_for_largest_palindrome() { 39 | assert_eq!(max(&get_palindrome_products(15, 15)), None); 40 | } 41 | 42 | #[test] 43 | fn error_smallest_palindrome_when_min_gt_max() { 44 | assert_eq!(min(&get_palindrome_products(1000, 1)), None); 45 | } 46 | 47 | #[test] 48 | fn error_largest_palindrome_when_min_st_max() { 49 | assert_eq!(max(&get_palindrome_products(2, 1)), None); 50 | } 51 | -------------------------------------------------------------------------------- /pangram/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pangram" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /pangram/README.md: -------------------------------------------------------------------------------- 1 | # Pangram 2 | 3 | Determine if a sentence is a pangram. 4 | 5 | Determine if a sentence is a pangram. A pangram (Greek: παν γράμμα, pan gramma, 6 | "every letter") is a sentence using every letter of the alphabet at least once. 7 | The best known English pangram is "The quick brown fox jumps over the lazy dog." 8 | 9 | The alphabet used is ASCII, and case insensitive, from 'a' to 'z' 10 | inclusively. 11 | 12 | ## Rust Installation 13 | 14 | Refer to the [exercism help page][help-page] for Rust installation and learning 15 | resources. 16 | 17 | ## Writing the Code 18 | 19 | Execute the tests with: 20 | 21 | ```bash 22 | $ cargo test 23 | ``` 24 | 25 | All but the first test have been ignored. After you get the first test to 26 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 27 | to pass again. The test file is located in the `tests` directory. You can 28 | also remove the ignore flag from all the tests to get them to run all at once 29 | if you wish. 30 | 31 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 32 | haven't already, it will help you with organizing your files. 33 | 34 | ## Feedback, Issues, Pull Requests 35 | 36 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 37 | 38 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 39 | 40 | [help-page]: http://exercism.io/languages/rust 41 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 42 | 43 | ## Source 44 | 45 | Wikipedia [https://en.wikipedia.org/wiki/Pangram](https://en.wikipedia.org/wiki/Pangram) 46 | 47 | ## Submitting Incomplete Problems 48 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 49 | 50 | -------------------------------------------------------------------------------- /pangram/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn is_pangram(words: &str) -> bool { 2 | (b'a'..=b'z').all(|letter| words.to_lowercase().contains(letter as char)) 3 | } 4 | -------------------------------------------------------------------------------- /pangram/tests/pangram.rs: -------------------------------------------------------------------------------- 1 | extern crate pangram; 2 | 3 | use pangram::*; 4 | 5 | #[test] 6 | fn empty_strings_are_not_pangrams() { 7 | let sentence = ""; 8 | assert!(!is_pangram(&sentence)); 9 | } 10 | 11 | #[test] 12 | fn classic_pangram_is_a_pangram() { 13 | let sentence = "the quick brown fox jumps over the lazy dog"; 14 | assert!(is_pangram(&sentence)); 15 | } 16 | 17 | #[test] 18 | fn pangrams_must_have_all_letters() { 19 | let sentence = "a quick movement of the enemy will jeopardize five gunboats"; 20 | assert!(!is_pangram(&sentence)); 21 | } 22 | 23 | #[test] 24 | fn pangrams_must_have_all_letters_two() { 25 | let sentence = "the quick brown fish jumps over the lazy dog"; 26 | assert!(!is_pangram(&sentence)); 27 | } 28 | 29 | #[test] 30 | fn pangrams_must_include_z() { 31 | let sentence = "the quick brown fox jumps over the lay dog"; 32 | assert!(!is_pangram(&sentence)); 33 | } 34 | 35 | #[test] 36 | fn underscores_do_not_affect_pangrams() { 37 | let sentence = "the_quick_brown_fox_jumps_over_the_lazy_dog"; 38 | assert!(is_pangram(&sentence)); 39 | } 40 | 41 | #[test] 42 | fn numbers_do_not_affect_pangrams() { 43 | let sentence = "the 1 quick brown fox jumps over the 2 lazy dogs"; 44 | assert!(is_pangram(&sentence)); 45 | } 46 | 47 | #[test] 48 | fn numbers_can_not_replace_letters() { 49 | let sentence = "7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog"; 50 | assert!(!is_pangram(&sentence)); 51 | } 52 | 53 | #[test] 54 | fn capitals_and_punctuation_can_be_in_pangrams() { 55 | let sentence = "\"Five quacking Zephyrs jolt my wax bed.\""; 56 | assert!(is_pangram(&sentence)); 57 | } 58 | 59 | #[test] 60 | fn non_ascii_characters_can_be_in_pangrams() { 61 | let sentence = "Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich."; 62 | assert!(is_pangram(&sentence)); 63 | } 64 | -------------------------------------------------------------------------------- /parallel-letter-frequency/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["edition"] 2 | 3 | [package] 4 | edition = '2018' 5 | name = "parallel-letter-frequency" 6 | version = "0.0.0" 7 | authors = ["iain"] 8 | 9 | [dependencies] -------------------------------------------------------------------------------- /parallel-letter-frequency/examples_/message_passing.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc; 2 | use std::sync::mpsc::Sender; 3 | use std::{collections::HashMap, thread}; 4 | 5 | pub type LetterCount = HashMap; 6 | 7 | pub fn frequency<'a>(text: &'a [&'static str], num_workers: usize) -> LetterCount { 8 | if text.len() < 128 { 9 | return frequency_helper(text); 10 | } 11 | let (tx, rx) = mpsc::channel(); 12 | 13 | let chunks = build_chunks(text, num_workers) 14 | .into_iter() 15 | .filter(|chunk| !chunk.is_empty()) 16 | .collect::>(); 17 | let num_chunks = chunks.len(); 18 | 19 | for chunk in chunks { 20 | let tx1 = Sender::clone(&tx); 21 | thread::spawn(move || tx1.send(frequency_helper(&chunk)).unwrap()); 22 | } 23 | 24 | let mut results = vec![]; 25 | for _ in 0..num_chunks { 26 | results.push(rx.recv().unwrap()); 27 | } 28 | 29 | results 30 | .into_iter() 31 | .fold(HashMap::new(), |acc, value| add(acc, value)) 32 | } 33 | 34 | fn build_chunks<'a>(text: &[&'static str], num_workers: usize) -> Vec> { 35 | let mut chunks: Vec> = Vec::new(); 36 | for _ in 0..num_workers { 37 | chunks.push(Vec::new()); 38 | } 39 | let mut index = 0; 40 | for line in text.iter() { 41 | chunks[index].push(line); 42 | index = (index + 1) % num_workers; 43 | } 44 | chunks 45 | } 46 | 47 | fn add(mut list1: LetterCount, list2: LetterCount) -> LetterCount { 48 | for (key, value) in list2 { 49 | let mut counter = list1.entry(key).or_insert(0); 50 | *counter += value; 51 | } 52 | list1 53 | } 54 | 55 | pub fn frequency_helper(texts: &[&str]) -> LetterCount { 56 | let mut map = HashMap::new(); 57 | 58 | for line in texts { 59 | for chr in line.chars().filter(|c| c.is_alphabetic()) { 60 | if let Some(c) = chr.to_lowercase().next() { 61 | (*map.entry(c).or_insert(0)) += 1; 62 | } 63 | } 64 | } 65 | map 66 | } 67 | -------------------------------------------------------------------------------- /parallel-letter-frequency/examples_/rayon.rs: -------------------------------------------------------------------------------- 1 | use rayon::{prelude::*, ThreadPoolBuilder}; 2 | use std::collections::HashMap; 3 | 4 | pub fn frequency_helper(line: &str) -> HashMap { 5 | let mut map = HashMap::new(); 6 | 7 | for chr in line.chars().filter(|c| c.is_alphabetic()) { 8 | if let Some(c) = chr.to_lowercase().next() { 9 | (*map.entry(c).or_insert(0)) += 1; 10 | } 11 | } 12 | map 13 | } 14 | 15 | fn add(mut list1: HashMap, list2: HashMap) -> HashMap { 16 | for (key, value) in list2 { 17 | let mut counter = list1.entry(key).or_insert(0); 18 | *counter += value; 19 | } 20 | list1 21 | } 22 | 23 | pub fn frequency(texts: &[&str], num_workers: usize) -> HashMap { 24 | ThreadPoolBuilder::new().num_threads(num_workers); 25 | texts 26 | .par_iter() 27 | .map(|text| frequency_helper(text)) 28 | .reduce_with(add) 29 | .unwrap_or(HashMap::new()) 30 | } 31 | -------------------------------------------------------------------------------- /parallel-letter-frequency/examples_/thread_handles.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, thread}; 2 | 3 | pub type LetterCount = HashMap; 4 | 5 | pub fn frequency<'a>(text: &'a [&'static str], num_workers: usize) -> LetterCount { 6 | let mut handles: Vec> = Vec::new(); 7 | 8 | for chunk in build_chunks(text, num_workers) { 9 | handles.push(thread::spawn(move || frequency_helper(&chunk))); 10 | } 11 | 12 | handles 13 | .into_iter() 14 | .map(|handle| handle.join().unwrap()) 15 | .fold(HashMap::new(), |acc, value| add(acc, value)) 16 | } 17 | 18 | fn build_chunks<'a>(text: &[&'static str], num_workers: usize) -> Vec> { 19 | let mut chunks: Vec> = Vec::new(); 20 | for _ in 0..num_workers { 21 | chunks.push(Vec::new()); 22 | } 23 | let mut index = 0; 24 | for line in text.iter() { 25 | chunks[index].push(line); 26 | index = (index + 1) % num_workers; 27 | } 28 | chunks 29 | } 30 | 31 | fn add(mut list1: LetterCount, list2: LetterCount) -> LetterCount { 32 | for (key, value) in list2 { 33 | let mut counter = list1.entry(key).or_insert(0); 34 | *counter += value; 35 | } 36 | list1 37 | } 38 | 39 | pub fn frequency_helper(texts: &[&str]) -> LetterCount { 40 | let mut map = HashMap::new(); 41 | 42 | for line in texts { 43 | for chr in line.chars().filter(|c| c.is_alphabetic()) { 44 | if let Some(c) = chr.to_lowercase().next() { 45 | (*map.entry(c).or_insert(0)) += 1; 46 | } 47 | } 48 | } 49 | map 50 | } 51 | -------------------------------------------------------------------------------- /parallel-letter-frequency/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc; 2 | use std::sync::mpsc::Sender; 3 | use std::{collections::HashMap, thread}; 4 | 5 | type LetterCount = HashMap; 6 | 7 | pub fn frequency<'a>(text: &'a [&'static str], num_workers: usize) -> LetterCount { 8 | if text.len() < 32 { 9 | return frequency_helper(text); 10 | } 11 | 12 | let (tx, rx) = mpsc::channel(); 13 | for chunk in build_chunks(text, num_workers) { 14 | let tx1 = Sender::clone(&tx); 15 | thread::spawn(move || tx1.send(frequency_helper(&chunk)).unwrap()); 16 | } 17 | 18 | rx.into_iter() 19 | .take(num_workers) 20 | .fold(HashMap::new(), |acc, value| add(acc, value)) 21 | } 22 | 23 | fn build_chunks<'a>(text: &[&'static str], num_workers: usize) -> Vec> { 24 | let mut chunks: Vec> = Vec::new(); 25 | for _ in 0..num_workers { 26 | chunks.push(Vec::new()); 27 | } 28 | let mut index = 0; 29 | for line in text.iter() { 30 | chunks[index].push(line); 31 | index = (index + 1) % num_workers; 32 | } 33 | chunks 34 | } 35 | 36 | fn add(mut list1: LetterCount, list2: LetterCount) -> LetterCount { 37 | for (key, value) in list2 { 38 | let mut counter = list1.entry(key).or_insert(0); 39 | *counter += value; 40 | } 41 | list1 42 | } 43 | 44 | fn frequency_helper(texts: &[&str]) -> LetterCount { 45 | let mut map = HashMap::new(); 46 | 47 | for line in texts { 48 | for chr in line.chars().filter(|c| c.is_alphabetic()) { 49 | if let Some(c) = chr.to_lowercase().next() { 50 | (*map.entry(c).or_insert(0)) += 1; 51 | } 52 | } 53 | } 54 | map 55 | } 56 | -------------------------------------------------------------------------------- /pascals-triangle/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pascals-triangle" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /pascals-triangle/README.md: -------------------------------------------------------------------------------- 1 | # Pascals Triangle 2 | 3 | Compute Pascal's triangle up to a given number of rows. 4 | 5 | In Pascal's Triangle each number is computed by adding the numbers to 6 | the right and left of the current position in the previous row. 7 | 8 | ```plain 9 | 1 10 | 1 1 11 | 1 2 1 12 | 1 3 3 1 13 | 1 4 6 4 1 14 | # ... etc 15 | ``` 16 | 17 | ## Rust Installation 18 | 19 | Refer to the [exercism help page][help-page] for Rust installation and learning 20 | resources. 21 | 22 | ## Writing the Code 23 | 24 | Execute the tests with: 25 | 26 | ```bash 27 | $ cargo test 28 | ``` 29 | 30 | All but the first test have been ignored. After you get the first test to 31 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 32 | to pass again. The test file is located in the `tests` directory. You can 33 | also remove the ignore flag from all the tests to get them to run all at once 34 | if you wish. 35 | 36 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 37 | haven't already, it will help you with organizing your files. 38 | 39 | ## Feedback, Issues, Pull Requests 40 | 41 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 42 | 43 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 44 | 45 | [help-page]: http://exercism.io/languages/rust 46 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 47 | 48 | ## Source 49 | 50 | Pascal's Triangle at Wolfram Math World [http://mathworld.wolfram.com/PascalsTriangle.html](http://mathworld.wolfram.com/PascalsTriangle.html) 51 | 52 | ## Submitting Incomplete Problems 53 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 54 | 55 | -------------------------------------------------------------------------------- /pascals-triangle/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub struct PascalsTriangle { 2 | rows: Vec>, 3 | } 4 | 5 | impl PascalsTriangle { 6 | pub fn new(row_count: u32) -> Self { 7 | let mut rows: Vec> = Vec::new(); 8 | let mut row = vec![1]; 9 | for _ in 0..(row_count) { 10 | rows.push(row.clone()); 11 | row = next(&row); 12 | } 13 | PascalsTriangle { rows } 14 | } 15 | 16 | pub fn rows(&self) -> Vec> { 17 | self.rows.clone() 18 | } 19 | } 20 | 21 | fn next(curr: &Vec) -> Vec { 22 | let mut next = vec![1]; 23 | for chunk in curr.windows(2) { 24 | next.push(chunk.iter().sum::()); 25 | } 26 | next.push(1); 27 | next 28 | } 29 | -------------------------------------------------------------------------------- /pascals-triangle/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate pascals_triangle; 2 | 3 | use pascals_triangle::*; 4 | 5 | fn main() { 6 | let pt = PascalsTriangle::new(1); 7 | print!("{:?}", pt.rows()); 8 | } 9 | -------------------------------------------------------------------------------- /pascals-triangle/tests/pascals-triangle.rs: -------------------------------------------------------------------------------- 1 | extern crate pascals_triangle; 2 | 3 | use pascals_triangle::*; 4 | 5 | #[test] 6 | fn no_rows() { 7 | let pt = PascalsTriangle::new(0); 8 | let expected: Vec> = Vec::new(); 9 | assert_eq!(expected, pt.rows()); 10 | } 11 | 12 | #[test] 13 | fn one_row() { 14 | let pt = PascalsTriangle::new(1); 15 | let expected: Vec> = vec![vec![1]]; 16 | assert_eq!(expected, pt.rows()); 17 | } 18 | 19 | #[test] 20 | fn two_rows() { 21 | let pt = PascalsTriangle::new(2); 22 | let expected: Vec> = vec![vec![1], vec![1, 1]]; 23 | assert_eq!(expected, pt.rows()); 24 | } 25 | 26 | #[test] 27 | fn three_rows() { 28 | let pt = PascalsTriangle::new(3); 29 | let expected: Vec> = vec![vec![1], vec![1, 1], vec![1, 2, 1]]; 30 | assert_eq!(expected, pt.rows()); 31 | } 32 | 33 | #[test] 34 | fn last_of_four_rows() { 35 | let pt = PascalsTriangle::new(4); 36 | let expected: Vec = vec![1, 3, 3, 1]; 37 | assert_eq!(expected, pt.rows().pop().unwrap()); 38 | } 39 | -------------------------------------------------------------------------------- /perfect-numbers/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /perfect-numbers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "perfect_numbers" 3 | version = "1.1.0" 4 | 5 | [dependencies] 6 | -------------------------------------------------------------------------------- /perfect-numbers/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Eq)] 2 | pub enum Classification { 3 | Abundant, 4 | Perfect, 5 | Deficient, 6 | } 7 | 8 | use Classification::*; 9 | 10 | pub fn classify(num: u64) -> Option { 11 | if num == 0 { 12 | return None; 13 | } 14 | let sum: u64 = (1..num).filter(|value| num % value == 0).sum(); 15 | match sum { 16 | sum if sum == num => Some(Perfect), 17 | sum if sum > num => Some(Abundant), 18 | _ => Some(Deficient), 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /perfect-numbers/tests/perfect-numbers.rs: -------------------------------------------------------------------------------- 1 | extern crate perfect_numbers; 2 | 3 | use perfect_numbers::{classify, Classification}; 4 | 5 | macro_rules! tests { 6 | ($property_test_func:ident { 7 | $( $(#[$attr:meta])* $test_name:ident( $( $param:expr ),* ); )+ 8 | }) => { 9 | $( 10 | $(#[$attr])* 11 | #[test] 12 | fn $test_name() { 13 | $property_test_func($( $param ),* ) 14 | } 15 | )+ 16 | } 17 | } 18 | 19 | fn test_classification(num: u64, result: Classification) { 20 | assert_eq!(classify(num), Some(result)); 21 | } 22 | 23 | #[test] 24 | fn basic() { 25 | assert_eq!(classify(0), None); 26 | } 27 | 28 | tests! { 29 | test_classification { 30 | test_1(1, Classification::Deficient); 31 | test_2(2, Classification::Deficient); 32 | test_4(4, Classification::Deficient); 33 | test_6(6, Classification::Perfect); 34 | test_12(12, Classification::Abundant); 35 | test_28(28, Classification::Perfect); 36 | test_30(30, Classification::Abundant); 37 | test_32(32, Classification::Deficient); 38 | test_33550335(33550335, Classification::Abundant); 39 | test_33550336(33550336, Classification::Perfect); 40 | test_33550337(33550337, Classification::Deficient); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /phone-number/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /phone-number/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "phone-number" 3 | version = "1.2.0" 4 | 5 | [dependencies] 6 | regex = "0.2" -------------------------------------------------------------------------------- /phone-number/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate regex; 2 | 3 | use regex::Regex; 4 | 5 | pub fn number(text: &str) -> Option { 6 | let re = Regex::new( 7 | r"(?x) # Free Spacing Mode 8 | ^[+1]?1?\s* 9 | [(]? 10 | (?P[2-9]\d{2}) # Area 11 | [)]? 12 | \s*[-.]* 13 | (?P[2-9]\d{2}) # Exchange 14 | \s*[-.]* 15 | (?P\d{4}) # Subscriber 16 | \s*$", 17 | ).unwrap(); 18 | 19 | match re.captures(text) { 20 | Some(caps) => { 21 | let area = &caps["area"]; 22 | let exchange = &caps["exchange"]; 23 | let subscriber = &caps["subscriber"]; 24 | Some(format!("{}{}{}", area, exchange, subscriber)) 25 | } 26 | _ => None, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /phone-number/tests/phone-number.rs: -------------------------------------------------------------------------------- 1 | extern crate phone_number as phone; 2 | 3 | fn to_some_string(s: &str) -> Option { 4 | Some(s.to_string()) 5 | } 6 | 7 | #[test] 8 | fn test_cleans_the_number() { 9 | assert_eq!( 10 | phone::number("(223) 456-7890"), 11 | to_some_string("2234567890") 12 | ); 13 | } 14 | 15 | #[test] 16 | fn test_cleans_numbers_with_dots() { 17 | assert_eq!(phone::number("223.456.7890"), to_some_string("2234567890")); 18 | } 19 | 20 | #[test] 21 | fn test_cleans_numbers_with_multiple_spaces() { 22 | assert_eq!( 23 | phone::number("223 456 7890 "), 24 | to_some_string("2234567890") 25 | ); 26 | } 27 | 28 | #[test] 29 | fn test_invalid_when_9_digits() { 30 | assert_eq!(phone::number("123456789"), None); 31 | } 32 | 33 | #[test] 34 | fn test_invalid_when_11_digits_does_not_start_with_a_1() { 35 | assert_eq!(phone::number("22234567890"), None); 36 | } 37 | 38 | #[test] 39 | fn test_valid_when_11_digits_and_starting_with_1() { 40 | assert_eq!(phone::number("12234567890"), to_some_string("2234567890")); 41 | } 42 | 43 | #[test] 44 | fn test_valid_when_11_digits_and_starting_with_1_even_with_punctuation() { 45 | assert_eq!( 46 | phone::number("+1 (223) 456-7890"), 47 | to_some_string("2234567890") 48 | ); 49 | } 50 | 51 | #[test] 52 | fn test_invalid_when_more_than_11_digits() { 53 | assert_eq!(phone::number("321234567890"), None); 54 | } 55 | 56 | #[test] 57 | fn test_invalid_with_letters() { 58 | assert_eq!(phone::number("123-abc-7890"), None); 59 | } 60 | 61 | #[test] 62 | fn test_invalid_with_punctuations() { 63 | assert_eq!(phone::number("123-@:!-7890"), None); 64 | } 65 | 66 | #[test] 67 | fn test_invalid_if_area_code_does_not_start_with_2_9() { 68 | assert_eq!(phone::number("(123) 456-7890"), None); 69 | } 70 | 71 | #[test] 72 | fn test_invalid_if_exchange_code_does_not_start_with_2_9() { 73 | assert_eq!(phone::number("(223) 056-7890"), None); 74 | } 75 | -------------------------------------------------------------------------------- /pig-latin/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /pig-latin/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense para saber los atributos posibles. 3 | // Mantenga el puntero para ver las descripciones de los existentes atributos 4 | // Para más información, visite: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug tests in pig-latin", 11 | "cargo": { 12 | "args": [ 13 | "test", 14 | "--no-run", 15 | "--lib" 16 | ] 17 | }, 18 | "args": [], 19 | "cwd": "${workspaceFolder}" 20 | }, 21 | { 22 | "type": "lldb", 23 | "request": "launch", 24 | "name": "Debug pig-latin", 25 | "cargo": { 26 | "args": [ 27 | "build", 28 | "--bin=pig-latin" 29 | ] 30 | }, 31 | "args": [], 32 | "cwd": "${workspaceFolder}" 33 | }, 34 | { 35 | "type": "lldb", 36 | "request": "launch", 37 | "name": "Debug tests in pig-latin", 38 | "cargo": { 39 | "args": [ 40 | "test", 41 | "--no-run", 42 | "--bin=pig-latin" 43 | ] 44 | }, 45 | "args": [], 46 | "cwd": "${workspaceFolder}" 47 | }, 48 | { 49 | "type": "lldb", 50 | "request": "launch", 51 | "name": "Debug pig-latin", 52 | "cargo": { 53 | "args": [ 54 | "build", 55 | "--test=pig-latin" 56 | ] 57 | }, 58 | "args": [], 59 | "cwd": "${workspaceFolder}" 60 | }, 61 | { 62 | "type": "lldb", 63 | "request": "launch", 64 | "name": "Debug tests in pig-latin", 65 | "cargo": { 66 | "args": [ 67 | "test", 68 | "--no-run", 69 | "--test=pig-latin" 70 | ] 71 | }, 72 | "args": [], 73 | "cwd": "${workspaceFolder}" 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /pig-latin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pig-latin" 3 | version = "1.0.0" 4 | authors = ["sacherjj "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /pig-latin/src/lib.rs: -------------------------------------------------------------------------------- 1 | fn translate_helper(word: &str) -> String { 2 | let letters: Vec = word.chars().map(|letter| letter.to_string()).collect(); 3 | let vowels = vec!["a", "e", "i", "o", "u"]; 4 | let first_letter = &word[0..1]; 5 | let first_two_letters = &word[0..2]; 6 | 7 | if vowels.contains(&first_letter) || first_two_letters == "xr" || first_two_letters == "yt" { 8 | return word.to_string() + "ay"; 9 | } 10 | let mut first_vowel_position = letters 11 | .iter() 12 | .position(|letter| vowels.contains(&letter.as_str())) 13 | .unwrap(); 14 | if letters[first_vowel_position - 1] == "q" && letters[first_vowel_position] == "u" { 15 | first_vowel_position += 1; 16 | } 17 | let prefix = &word[0..first_vowel_position]; 18 | let suffix = &word[first_vowel_position..]; 19 | 20 | suffix.to_string() + prefix + "ay" 21 | } 22 | 23 | pub fn translate(text: &str) -> String { 24 | text 25 | .split_whitespace() 26 | .map(translate_helper) 27 | .collect::>() 28 | .join(" ") 29 | } 30 | -------------------------------------------------------------------------------- /poker/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /poker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "poker" 3 | version = "1.1.0" 4 | authors = ["Peter Goodspeed-Niklaus "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /poker/src/card.rs: -------------------------------------------------------------------------------- 1 | use std::{cmp::Ordering, fmt}; 2 | 3 | #[derive(Clone, Debug, PartialEq, Hash, PartialOrd, Eq, Ord)] 4 | pub enum Suit { 5 | Clubs, 6 | Diamonds, 7 | Hearts, 8 | Spades, 9 | } 10 | 11 | #[derive(Clone, Debug, PartialEq, PartialOrd, Hash, Eq, Ord)] 12 | pub enum Rank { 13 | N(u8), 14 | Jack, 15 | Queen, 16 | King, 17 | Ace, 18 | } 19 | 20 | #[derive(PartialEq, Clone, Hash, Eq, Ord)] 21 | pub struct Card { 22 | pub rank: Rank, 23 | pub suit: Suit, 24 | } 25 | 26 | impl Card { 27 | pub fn to_u8(&self) -> u8 { 28 | match self.rank { 29 | Rank::N(n) => n, 30 | Rank::Jack => 11, 31 | Rank::Queen => 12, 32 | Rank::King => 13, 33 | Rank::Ace => 14, 34 | } 35 | } 36 | } 37 | 38 | impl PartialOrd for Card { 39 | fn partial_cmp(&self, other: &Card) -> Option { 40 | Some(self.rank.cmp(&other.rank)) 41 | } 42 | } 43 | 44 | impl<'a> Into for &'a str { 45 | fn into(self) -> Card { 46 | let mut as_vector: Vec = self.chars().collect(); 47 | let suit = as_vector.pop().unwrap(); 48 | let rank = as_vector.into_iter().nth(0).unwrap(); 49 | 50 | let rank = match rank { 51 | '2'...'9' => Rank::N(rank.to_digit(10).unwrap() as u8), 52 | '1' => Rank::N(10u8), 53 | 'J' => Rank::Jack, 54 | 'Q' => Rank::Queen, 55 | 'K' => Rank::King, 56 | 'A' => Rank::Ace, 57 | value => panic!("Invalid value: {}", value), 58 | }; 59 | let suit = match suit { 60 | 'C' => Suit::Clubs, 61 | 'D' => Suit::Diamonds, 62 | 'H' => Suit::Hearts, 63 | 'S' => Suit::Spades, 64 | suit => panic!("Invalid suit: {}", suit), 65 | }; 66 | Card { rank, suit } 67 | } 68 | } 69 | 70 | impl fmt::Debug for Card { 71 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 72 | write!(f, "<{:?} of {:?}>", self.rank, self.suit) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /poker/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod card; 2 | mod hand; 3 | 4 | use hand::*; 5 | use std::cmp::Ordering; 6 | 7 | pub fn winning_hands<'a>(hands: &[&'a str]) -> Option> { 8 | let mut player_hands: Vec = hands.iter().map(|&text| Hand::from(text)).collect(); 9 | player_hands.sort_by(|a, b| a.partial_cmp(b).unwrap()); 10 | player_hands.last().map(|winner| { 11 | player_hands 12 | .iter() 13 | .rev() 14 | .take_while(|&hand| hand.partial_cmp(winner) == Some(Ordering::Equal)) 15 | .map(|hand| hand.text) 16 | .collect() 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /prime-factors/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /prime-factors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prime_factors" 3 | version = "1.1.0" 4 | authors = ["sacherjj "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /prime-factors/src/lib.rs: -------------------------------------------------------------------------------- 1 | fn find_factors(n: u64, mut factors: Vec) -> Vec { 2 | if n < 2 { 3 | return factors; 4 | } 5 | 6 | let range = (2..).take_while(|x| x * x <= n); 7 | for value in range { 8 | if n % value == 0 { 9 | factors.push(value); 10 | return find_factors(n / value, factors); 11 | } 12 | } 13 | factors.push(n); 14 | factors 15 | } 16 | 17 | pub fn factors(n: u64) -> Vec { 18 | find_factors(n, Vec::new()) 19 | } 20 | -------------------------------------------------------------------------------- /prime-factors/tests/prime-factors.rs: -------------------------------------------------------------------------------- 1 | extern crate prime_factors; 2 | 3 | use prime_factors::factors; 4 | 5 | #[test] 6 | fn test_no_factors() { 7 | assert_eq!(factors(1), vec![]); 8 | } 9 | 10 | #[test] 11 | fn test_prime_number() { 12 | assert_eq!(factors(2), vec![2]); 13 | } 14 | 15 | #[test] 16 | fn test_square_of_a_prime() { 17 | assert_eq!(factors(9), vec![3, 3]); 18 | } 19 | 20 | #[test] 21 | fn test_cube_of_a_prime() { 22 | assert_eq!(factors(8), vec![2, 2, 2]); 23 | } 24 | 25 | #[test] 26 | fn test_product_of_primes_and_non_primes() { 27 | assert_eq!(factors(12), vec![2, 2, 3]); 28 | } 29 | 30 | #[test] 31 | fn test_product_of_primes() { 32 | assert_eq!(factors(901255), vec![5, 17, 23, 461]); 33 | } 34 | 35 | #[test] 36 | fn test_factors_include_large_prime() { 37 | assert_eq!(factors(93819012551), vec![11, 9539, 894119]); 38 | } 39 | -------------------------------------------------------------------------------- /protein-translation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "protein-translation" 3 | version = "0.1.0" -------------------------------------------------------------------------------- /protein-translation/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | pub struct Proteins { 4 | data: HashMap<&'static str, &'static str>, 5 | } 6 | 7 | impl Proteins { 8 | pub fn name_for(&self, codon: &str) -> Result<&'static str, &'static str> { 9 | match self.data.get(codon) { 10 | Some(protein) => Ok(*protein), 11 | None => Err("No match found"), 12 | } 13 | } 14 | pub fn of_rna(&self, codons: &str) -> Result, &'static str> { 15 | let codons: Vec = codons 16 | .chars() 17 | .map(|ch| ch.to_string()) 18 | .collect::>() 19 | .chunks(3) 20 | .map(|chunk| chunk.join("")) 21 | .collect(); 22 | let mut proteins = Vec::new(); 23 | for codon in codons { 24 | let protein = self.name_for(&codon)?; 25 | if protein == "stop codon" { 26 | return Ok(proteins); 27 | } 28 | proteins.push(protein); 29 | } 30 | Ok(proteins) 31 | } 32 | } 33 | 34 | pub fn parse(pairs: Vec<(&'static str, &'static str)>) -> Proteins { 35 | let mut data = HashMap::new(); 36 | for (key, value) in pairs { 37 | data.insert(key, value); 38 | } 39 | Proteins { data } 40 | } 41 | -------------------------------------------------------------------------------- /proverb/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /proverb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "proverb" 3 | version = "0.0.0" 4 | authors = ["sacherjj "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /proverb/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn build_proverb(list: Vec<&str>) -> String { 2 | let mut output: String = String::new(); 3 | if list.is_empty() { 4 | return output; 5 | } 6 | for pair in list.windows(2) { 7 | if let [first, second] = pair { 8 | output += &format!("For want of a {} the {} was lost.\n", first, second); 9 | } 10 | } 11 | output += &format!("And all for the want of a {}.", list[0]); 12 | output 13 | } 14 | -------------------------------------------------------------------------------- /proverb/tests/proverb.rs: -------------------------------------------------------------------------------- 1 | extern crate proverb; 2 | 3 | use proverb::build_proverb; 4 | 5 | #[test] 6 | fn test_two_pieces() { 7 | let input = vec!["nail", "shoe"]; 8 | let expected = vec![ 9 | "For want of a nail the shoe was lost.", 10 | "And all for the want of a nail.", 11 | ].join("\n"); 12 | assert_eq!(build_proverb(input), expected); 13 | } 14 | 15 | // Notice the change in the last line at three pieces. 16 | #[test] 17 | fn test_three_pieces() { 18 | let input = vec!["nail", "shoe", "horse"]; 19 | let expected = vec![ 20 | "For want of a nail the shoe was lost.", 21 | "For want of a shoe the horse was lost.", 22 | "And all for the want of a nail.", 23 | ].join("\n"); 24 | assert_eq!(build_proverb(input), expected); 25 | } 26 | 27 | #[test] 28 | fn test_one_piece() { 29 | let input = vec!["nail"]; 30 | let expected = String::from("And all for the want of a nail."); 31 | assert_eq!(build_proverb(input), expected); 32 | } 33 | 34 | #[test] 35 | fn test_zero_pieces() { 36 | let input: Vec<&str> = vec![]; 37 | let expected = String::new(); 38 | assert_eq!(build_proverb(input), expected); 39 | } 40 | 41 | #[test] 42 | fn test_full() { 43 | let input = vec![ 44 | "nail", "shoe", "horse", "rider", "message", "battle", "kingdom", 45 | ]; 46 | let expected = vec![ 47 | "For want of a nail the shoe was lost.", 48 | "For want of a shoe the horse was lost.", 49 | "For want of a horse the rider was lost.", 50 | "For want of a rider the message was lost.", 51 | "For want of a message the battle was lost.", 52 | "For want of a battle the kingdom was lost.", 53 | "And all for the want of a nail.", 54 | ].join("\n"); 55 | assert_eq!(build_proverb(input), expected); 56 | } 57 | 58 | #[test] 59 | fn test_three_pieces_modernized() { 60 | let input = vec!["pin", "gun", "soldier", "battle"]; 61 | let expected = vec![ 62 | "For want of a pin the gun was lost.", 63 | "For want of a gun the soldier was lost.", 64 | "For want of a soldier the battle was lost.", 65 | "And all for the want of a pin.", 66 | ].join("\n"); 67 | assert_eq!(build_proverb(input), expected); 68 | } 69 | -------------------------------------------------------------------------------- /pythagorean-triplet/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /pythagorean-triplet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pythagorean_triplet" 3 | version = "0.0.0" 4 | authors = ["sacherjj "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /pythagorean-triplet/README.md: -------------------------------------------------------------------------------- 1 | # Pythagorean Triplet 2 | 3 | A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for 4 | which, 5 | 6 | ```text 7 | a**2 + b**2 = c**2 8 | ``` 9 | 10 | For example, 11 | 12 | ```text 13 | 3**2 + 4**2 = 9 + 16 = 25 = 5**2. 14 | ``` 15 | 16 | There exists exactly one Pythagorean triplet for which a + b + c = 1000. 17 | 18 | Find the product a * b * c. 19 | 20 | ## Rust Installation 21 | 22 | Refer to the [exercism help page][help-page] for Rust installation and learning 23 | resources. 24 | 25 | ## Writing the Code 26 | 27 | Execute the tests with: 28 | 29 | ```bash 30 | $ cargo test 31 | ``` 32 | 33 | All but the first test have been ignored. After you get the first test to 34 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 35 | to pass again. The test file is located in the `tests` directory. You can 36 | also remove the ignore flag from all the tests to get them to run all at once 37 | if you wish. 38 | 39 | Make sure to read the [Modules](https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html) chapter if you 40 | haven't already, it will help you with organizing your files. 41 | 42 | ## Feedback, Issues, Pull Requests 43 | 44 | The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help! 45 | 46 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md). 47 | 48 | [help-page]: http://exercism.io/languages/rust 49 | [modules]: https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html 50 | [cargo]: https://doc.rust-lang.org/book/second-edition/ch14-00-more-about-cargo.html 51 | 52 | ## Source 53 | 54 | Problem 9 at Project Euler [http://projecteuler.net/problem=9](http://projecteuler.net/problem=9) 55 | 56 | ## Submitting Incomplete Solutions 57 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 58 | -------------------------------------------------------------------------------- /pythagorean-triplet/src/lib.rs: -------------------------------------------------------------------------------- 1 | /// Generates a Pythagorean soltuion for the given side a 2 | /// http://www.friesian.com/pythag.htm 3 | fn calculate_sides(a: u32) -> (u32, u32, u32) { 4 | let is_even = |x| x % 2 == 0; 5 | if is_even(a) { 6 | let b = (a / 2).pow(2) - 1; 7 | (a, b, b + 2) 8 | } else { 9 | let b = (a.pow(2) - 1) / 2; 10 | (a, b, b + 1) 11 | } 12 | } 13 | 14 | fn triplet(n: u32, sides: (u32, u32, u32)) -> (u32, u32, u32) { 15 | let (x, y, z) = sides; 16 | let a = x * n * n; 17 | let b = y * n * n; 18 | let c = z * n * n; 19 | assert_eq!(a * a + b * b, c * c); 20 | (a, b, c) 21 | } 22 | 23 | pub fn find() -> Option { 24 | for m in 3.. { 25 | let sides = calculate_sides(m); 26 | for n in 1.. { 27 | let (a, b, c) = triplet(n, sides); 28 | let sum = a + b + c; 29 | if sum == 1000 { 30 | return Some(a * b * c); 31 | } else if sum > 1000 { 32 | break; 33 | } 34 | } 35 | } 36 | None 37 | } 38 | -------------------------------------------------------------------------------- /pythagorean-triplet/tests/pythagorean-triplet.rs: -------------------------------------------------------------------------------- 1 | extern crate pythagorean_triplet; 2 | 3 | #[test] 4 | fn test_answer() { 5 | assert_eq!(pythagorean_triplet::find(), Some(31875000)); 6 | } 7 | -------------------------------------------------------------------------------- /queen-attack/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "queen-attack" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /queen-attack/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug)] 2 | pub struct ChessPosition { 3 | row: i8, 4 | col: i8, 5 | } 6 | 7 | pub struct Queen { 8 | pos: ChessPosition, 9 | } 10 | 11 | impl ChessPosition { 12 | pub fn new(row: i8, col: i8) -> Result { 13 | if row < 0 || col < 0 || row >= 8 || col >= 8 { 14 | return Err("Invalid Parameters!".into()); 15 | } 16 | Ok(ChessPosition { row, col }) 17 | } 18 | } 19 | 20 | impl Queen { 21 | pub fn new(pos: ChessPosition) -> Self { 22 | Queen { pos: pos } 23 | } 24 | 25 | pub fn can_attack(self, queen: &Queen) -> bool { 26 | self.pos.col == queen.pos.col || self.pos.row == queen.pos.row 27 | || Self::on_diagonal(&self.pos, &queen.pos) 28 | } 29 | 30 | fn on_diagonal(pos1: &ChessPosition, pos2: &ChessPosition) -> bool { 31 | let delta_x = (pos1.col - pos2.col).abs(); 32 | let delta_y = (pos1.row - pos2.row).abs(); 33 | delta_x == delta_y 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /raindrops/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "raindrops" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /raindrops/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn raindrops(number: u32) -> String { 2 | let mut s = String::new(); 3 | 4 | if number % 3 == 0 { 5 | s += "Pling"; 6 | } 7 | if number % 5 == 0 { 8 | s += "Plang"; 9 | } 10 | if number % 7 == 0 { 11 | s += "Plong"; 12 | } 13 | 14 | match s.as_ref() { 15 | "" => number.to_string(), 16 | _ => s, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /raindrops/tests/raindrops.rs: -------------------------------------------------------------------------------- 1 | extern crate raindrops; 2 | 3 | #[test] 4 | fn test_1() { assert_eq!("1", raindrops::raindrops(1)); } 5 | 6 | #[test] 7 | fn test_3() { assert_eq!("Pling", raindrops::raindrops(3)); } 8 | 9 | #[test] 10 | fn test_5() { assert_eq!("Plang", raindrops::raindrops(5)); } 11 | 12 | #[test] 13 | fn test_7() { assert_eq!("Plong", raindrops::raindrops(7)); } 14 | 15 | #[test] 16 | fn test_6() { assert_eq!("Pling", raindrops::raindrops(6)); } 17 | 18 | #[test] 19 | fn test_9() { assert_eq!("Pling", raindrops::raindrops(9)); } 20 | 21 | #[test] 22 | fn test_10() { assert_eq!("Plang", raindrops::raindrops(10)); } 23 | 24 | #[test] 25 | fn test_14() { assert_eq!("Plong", raindrops::raindrops(14)); } 26 | 27 | #[test] 28 | fn test_15() { assert_eq!("PlingPlang", raindrops::raindrops(15)); } 29 | 30 | #[test] 31 | fn test_21() { assert_eq!("PlingPlong", raindrops::raindrops(21)); } 32 | 33 | #[test] 34 | fn test_25() { assert_eq!("Plang", raindrops::raindrops(25)); } 35 | 36 | #[test] 37 | fn test_35() { assert_eq!("PlangPlong", raindrops::raindrops(35)); } 38 | 39 | #[test] 40 | fn test_49() { assert_eq!("Plong", raindrops::raindrops(49)); } 41 | 42 | #[test] 43 | fn test_52() { assert_eq!("52", raindrops::raindrops(52)); } 44 | 45 | #[test] 46 | fn test_105() { assert_eq!("PlingPlangPlong", raindrops::raindrops(105)); } 47 | 48 | #[test] 49 | fn test_12121() { assert_eq!("12121", raindrops::raindrops(12121)); } 50 | -------------------------------------------------------------------------------- /reverse-string/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Cargo.lock if creating a library 2 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /reverse-string/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reverse_string" 3 | version = "1.0.0" 4 | authors = ["Peter Goodspeed-Niklaus "] 5 | 6 | [features] 7 | grapheme = [] 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /reverse-string/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn reverse(input: &str) -> String { 2 | input.chars().rev().collect() 3 | } 4 | -------------------------------------------------------------------------------- /reverse-string/tests/reverse-string.rs: -------------------------------------------------------------------------------- 1 | //! Tests for reverse-string 2 | //! 3 | //! Generated by [script][script] using [canonical data][canonical-data] 4 | //! 5 | //! [script]: https://github.com/exercism/rust/blob/master/bin/init_exercise.py 6 | //! [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/reverse-string/canonical_data.json 7 | 8 | extern crate reverse_string; 9 | use reverse_string::*; 10 | 11 | /// Process a single test case for the property `reverse` 12 | fn process_reverse_case(input: &str, expected: &str) { 13 | assert_eq!(&reverse(input), expected) 14 | } 15 | 16 | #[test] 17 | /// empty string 18 | fn test_empty_string() { 19 | process_reverse_case("", ""); 20 | } 21 | 22 | #[test] 23 | /// a word 24 | fn test_a_word() { 25 | process_reverse_case("robot", "tobor"); 26 | } 27 | 28 | #[test] 29 | /// a capitalized word 30 | fn test_a_capitalized_word() { 31 | process_reverse_case("Ramen", "nemaR"); 32 | } 33 | 34 | #[test] 35 | /// a sentence with punctuation 36 | fn test_a_sentence_with_punctuation() { 37 | process_reverse_case("I'm hungry!", "!yrgnuh m'I"); 38 | } 39 | 40 | #[test] 41 | /// a palindrome 42 | fn test_a_palindrome() { 43 | process_reverse_case("racecar", "racecar"); 44 | } 45 | 46 | #[test] 47 | /// wide characters 48 | fn test_wide_characters() { 49 | process_reverse_case("子猫", "猫子"); 50 | } 51 | 52 | #[test] 53 | #[cfg(feature = "grapheme")] 54 | /// grapheme clusters 55 | fn test_grapheme_clusters() { 56 | process_reverse_case("uüu", "uüu"); 57 | } 58 | -------------------------------------------------------------------------------- /rna-transcription/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rna-transcription" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /rna-transcription/README.md: -------------------------------------------------------------------------------- 1 | # Rna Transcription 2 | 3 | Given a DNA strand, return its RNA complement (per RNA transcription). 4 | 5 | Both DNA and RNA strands are a sequence of nucleotides. 6 | 7 | The four nucleotides found in DNA are adenine (**A**), cytosine (**C**), 8 | guanine (**G**) and thymine (**T**). 9 | 10 | The four nucleotides found in RNA are adenine (**A**), cytosine (**C**), 11 | guanine (**G**) and uracil (**U**). 12 | 13 | Given a DNA strand, its transcribed RNA strand is formed by replacing 14 | each nucleotide with its complement: 15 | 16 | * `G` -> `C` 17 | * `C` -> `G` 18 | * `T` -> `A` 19 | * `A` -> `U` 20 | 21 | ## Rust Installation 22 | 23 | Refer to the [exercism help page][help-page] for Rust installation and learning 24 | resources. 25 | 26 | ## Writing the Code 27 | 28 | Execute the tests with: 29 | 30 | ```bash 31 | $ cargo test 32 | ``` 33 | 34 | All but the first test have been ignored. After you get the first test to 35 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 36 | to pass again. The test file is located in the `tests` directory. You can 37 | also remove the ignore flag from all the tests to get them to run all at once 38 | if you wish. 39 | 40 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 41 | haven't already, it will help you with organizing your files. 42 | 43 | ## Feedback, Issues, Pull Requests 44 | 45 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 46 | 47 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 48 | 49 | [help-page]: http://exercism.io/languages/rust 50 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 51 | 52 | ## Source 53 | 54 | Rosalind [http://rosalind.info/problems/rna](http://rosalind.info/problems/rna) 55 | 56 | ## Submitting Incomplete Problems 57 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 58 | 59 | -------------------------------------------------------------------------------- /rna-transcription/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub struct RibonucleicAcid { 3 | rna: String, 4 | } 5 | 6 | impl RibonucleicAcid { 7 | pub fn new(rna: &str) -> Self { 8 | RibonucleicAcid { rna: rna.into() } 9 | } 10 | } 11 | 12 | #[derive(Debug, PartialEq)] 13 | pub struct DeoxyribonucleicAcid { 14 | dna: String, 15 | } 16 | 17 | impl DeoxyribonucleicAcid { 18 | pub fn new(dna: &str) -> Self { 19 | DeoxyribonucleicAcid { dna: dna.into() } 20 | } 21 | 22 | pub fn to_rna(&self) -> RibonucleicAcid { 23 | let rna = self.dna.chars().map(convert).collect::(); 24 | 25 | RibonucleicAcid { rna } 26 | } 27 | } 28 | 29 | fn convert(chr: char) -> char { 30 | match chr { 31 | 'C' => 'G', 32 | 'G' => 'C', 33 | 'A' => 'U', 34 | 'T' => 'A', 35 | _ => chr, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rna-transcription/tests/rna-transcription.rs: -------------------------------------------------------------------------------- 1 | extern crate rna_transcription as dna; 2 | 3 | #[test] 4 | fn test_acid_equals_acid() { 5 | assert_eq!(dna::RibonucleicAcid::new("CGA"), 6 | dna::RibonucleicAcid::new("CGA")); 7 | assert!(dna::RibonucleicAcid::new("CGA") != dna::RibonucleicAcid::new("AGC")); 8 | } 9 | 10 | #[test] 11 | fn test_transcribes_cytosine_guanine() { 12 | assert_eq!(dna::RibonucleicAcid::new("G"), 13 | dna::DeoxyribonucleicAcid::new("C").to_rna()); 14 | } 15 | 16 | #[test] 17 | fn test_transcribes_guanine_cytosine() { 18 | assert_eq!(dna::RibonucleicAcid::new("C"), 19 | dna::DeoxyribonucleicAcid::new("G").to_rna()); 20 | } 21 | 22 | #[test] 23 | fn test_transcribes_adenine_uracil() { 24 | assert_eq!(dna::RibonucleicAcid::new("U"), 25 | dna::DeoxyribonucleicAcid::new("A").to_rna()); 26 | } 27 | 28 | #[test] 29 | fn test_transcribes_thymine_to_adenine() { 30 | assert_eq!(dna::RibonucleicAcid::new("A"), 31 | dna::DeoxyribonucleicAcid::new("T").to_rna()); 32 | } 33 | 34 | #[test] 35 | fn test_transcribes_all_dna_to_rna() { 36 | assert_eq!(dna::RibonucleicAcid::new("UGCACCAGAAUU"), 37 | dna::DeoxyribonucleicAcid::new("ACGTGGTCTTAA").to_rna()) 38 | } 39 | -------------------------------------------------------------------------------- /robot-name/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "robot-name" 3 | version = "0.0.0" 4 | 5 | [dependencies] 6 | rand = "0.5.0" -------------------------------------------------------------------------------- /robot-name/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate rand; 2 | 3 | use rand::prelude::*; 4 | 5 | #[derive(Default)] 6 | pub struct Robot { 7 | name: String, 8 | } 9 | 10 | fn get_random_letter() -> char { 11 | let alphabet: Vec<_> = (b'A'..=b'Z').collect(); 12 | let letters = alphabet.as_slice(); 13 | let mut rng = thread_rng(); 14 | letters[rng.gen_range(0, 26)] as char 15 | } 16 | 17 | fn get_random_number() -> u8 { 18 | thread_rng().gen_range(0, 10) 19 | } 20 | 21 | impl Robot { 22 | pub fn new() -> Robot { 23 | let mut name = String::default(); 24 | for _ in 0..2 { 25 | name += &get_random_letter().to_string(); 26 | } 27 | for _ in 0..3 { 28 | name += &get_random_number().to_string(); 29 | } 30 | Robot { name } 31 | } 32 | 33 | pub fn name(&self) -> &str { 34 | &self.name 35 | } 36 | 37 | pub fn reset_name(&mut self) { 38 | let new_robot = Self::new(); 39 | self.name = new_robot.name; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /robot-name/tests/robot-name.rs: -------------------------------------------------------------------------------- 1 | extern crate robot_name as robot; 2 | 3 | /* 4 | These are the expected signatures: 5 | 6 | impl Robot { 7 | pub fn new() -> Robot { ... } 8 | pub fn name<'a>(&'a self) -> &'a str { ... } 9 | pub fn reset_name(&mut self) { ... } 10 | } 11 | */ 12 | 13 | fn assert_name_matches_pattern(n: &str) { 14 | assert!(n.len() == 5, "name is exactly 5 characters long"); 15 | assert!( 16 | n[0..2].chars().all(|c| c >= 'A' && c <= 'Z'), 17 | "name starts with 2 uppercase letters" 18 | ); 19 | assert!( 20 | n[2..].chars().all(|c| c >= '0' && c <= '9'), 21 | "name ends with 3 numbers" 22 | ); 23 | } 24 | 25 | fn assert_name_is_persistent(r: &robot::Robot) { 26 | // The type system already proves this, but why not. 27 | let n1 = r.name(); 28 | let n2 = r.name(); 29 | let n3 = r.name(); 30 | assert_eq!(n1, n2); 31 | assert_eq!(n2, n3); 32 | } 33 | 34 | #[test] 35 | fn test_name_should_match_expected_pattern() { 36 | let r = robot::Robot::new(); 37 | assert_name_matches_pattern(r.name()); 38 | } 39 | 40 | #[test] 41 | fn test_name_is_persistent() { 42 | assert_name_is_persistent(&robot::Robot::new()); 43 | } 44 | 45 | #[test] 46 | fn test_different_robots_have_different_names() { 47 | let r1 = robot::Robot::new(); 48 | let r2 = robot::Robot::new(); 49 | assert_ne!(r1.name(), r2.name(), "Robot names should be different"); 50 | } 51 | 52 | #[test] 53 | fn test_new_name_should_match_expected_pattern() { 54 | let mut r = robot::Robot::new(); 55 | assert_name_matches_pattern(r.name()); 56 | r.reset_name(); 57 | assert_name_matches_pattern(r.name()); 58 | } 59 | 60 | #[test] 61 | fn test_new_name_is_persistent() { 62 | let mut r = robot::Robot::new(); 63 | r.reset_name(); 64 | assert_name_is_persistent(&r); 65 | } 66 | 67 | #[test] 68 | fn test_new_name_is_different_from_old_name() { 69 | let mut r = robot::Robot::new(); 70 | let n1 = r.name().to_string(); 71 | r.reset_name(); 72 | let n2 = r.name().to_string(); 73 | assert_ne!(n1, n2, "Robot name should change when reset"); 74 | } 75 | -------------------------------------------------------------------------------- /robot-simulator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "robot-simulator" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /robot-simulator/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(PartialEq, Debug)] 2 | pub enum Direction { 3 | North, 4 | East, 5 | South, 6 | West, 7 | } 8 | 9 | use Direction::*; 10 | 11 | pub struct Robot { 12 | x: isize, 13 | y: isize, 14 | direction: Direction, 15 | } 16 | 17 | impl Robot { 18 | pub fn new(x: isize, y: isize, d: Direction) -> Self { 19 | Robot { 20 | x: x, 21 | y: y, 22 | direction: d, 23 | } 24 | } 25 | 26 | pub fn turn_right(mut self) -> Self { 27 | self.direction = match self.direction { 28 | North => East, 29 | East => South, 30 | South => West, 31 | West => North, 32 | }; 33 | self 34 | } 35 | 36 | pub fn turn_left(mut self) -> Self { 37 | self.direction = match self.direction { 38 | North => West, 39 | West => South, 40 | South => East, 41 | East => North, 42 | }; 43 | self 44 | } 45 | 46 | pub fn advance(mut self) -> Self { 47 | match self.direction { 48 | North => self.y += 1, 49 | West => self.x += -1, 50 | South => self.y += -1, 51 | East => self.x += 1, 52 | }; 53 | self 54 | } 55 | 56 | pub fn instructions(mut self, instructions: &str) -> Self { 57 | for instruction in instructions.chars() { 58 | self = match instruction { 59 | 'L' => self.turn_left(), 60 | 'R' => self.turn_right(), 61 | 'A' => self.advance(), 62 | _ => self, // Contemplate killing all stupid humans 63 | } 64 | } 65 | self 66 | } 67 | 68 | pub fn position(&self) -> (isize, isize) { 69 | (self.x, self.y) 70 | } 71 | 72 | pub fn direction(&self) -> &Direction { 73 | &self.direction 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /roman-numerals/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "roman-numerals" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /roman-numerals/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub struct Roman { 2 | symbol: &'static str, 3 | value: u32, 4 | } 5 | 6 | const NUMERALS: [Roman; 13] = [ 7 | Roman { symbol: "I", value: 1 }, 8 | Roman { symbol: "IV", value: 4 }, 9 | Roman { symbol: "V", value: 5 }, 10 | Roman { symbol: "IX", value: 9 }, 11 | Roman { symbol: "X", value: 10 }, 12 | Roman { symbol: "XL", value: 40 }, 13 | Roman { symbol: "L", value: 50 }, 14 | Roman { symbol: "XC", value: 90 }, 15 | Roman { symbol: "C", value: 100 }, 16 | Roman { symbol: "CD", value: 400 }, 17 | Roman { symbol: "D", value: 500 }, 18 | Roman { symbol: "CM", value: 900 }, 19 | Roman { symbol: "M", value: 1000 }, 20 | ]; 21 | 22 | impl Roman { 23 | pub fn from(mut number: u32) -> String { 24 | let mut roman: String = String::new(); 25 | for numeral in NUMERALS.iter().rev() { 26 | while number >= numeral.value { 27 | roman += numeral.symbol; 28 | number -= numeral.value; 29 | } 30 | } 31 | roman 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /roman-numerals/tests/roman-numerals.rs: -------------------------------------------------------------------------------- 1 | extern crate roman_numerals; 2 | 3 | use roman_numerals::*; 4 | 5 | #[test] 6 | fn test_one() { 7 | assert_eq!("I", Roman::from(1).to_string()); 8 | } 9 | 10 | #[test] 11 | fn test_two() { 12 | assert_eq!("II", Roman::from(2).to_string()); 13 | } 14 | 15 | #[test] 16 | fn test_three() { 17 | assert_eq!("III", Roman::from(3).to_string()); 18 | } 19 | 20 | #[test] 21 | fn test_four() { 22 | assert_eq!("IV", Roman::from(4).to_string()); 23 | } 24 | 25 | #[test] 26 | fn test_five() { 27 | assert_eq!("V", Roman::from(5).to_string()); 28 | } 29 | 30 | #[test] 31 | fn test_six() { 32 | assert_eq!("VI", Roman::from(6).to_string()); 33 | } 34 | 35 | #[test] 36 | fn test_nine() { 37 | assert_eq!("IX", Roman::from(9).to_string()); 38 | } 39 | 40 | #[test] 41 | fn test_twenty_seven() { 42 | assert_eq!("XXVII", Roman::from(27).to_string()); 43 | } 44 | 45 | #[test] 46 | fn test_forty_eight() { 47 | assert_eq!("XLVIII", Roman::from(48).to_string()); 48 | } 49 | 50 | #[test] 51 | fn test_fifty_nine() { 52 | assert_eq!("LIX", Roman::from(59).to_string()); 53 | } 54 | 55 | #[test] 56 | fn test_ninety_three() { 57 | assert_eq!("XCIII", Roman::from(93).to_string()); 58 | } 59 | 60 | #[test] 61 | fn test_141() { 62 | assert_eq!("CXLI", Roman::from(141).to_string()); 63 | } 64 | 65 | #[test] 66 | fn test_163() { 67 | assert_eq!("CLXIII", Roman::from(163).to_string()); 68 | } 69 | 70 | #[test] 71 | fn test_402() { 72 | assert_eq!("CDII", Roman::from(402).to_string()); 73 | } 74 | 75 | #[test] 76 | fn test_575() { 77 | assert_eq!("DLXXV", Roman::from(575).to_string()); 78 | } 79 | 80 | #[test] 81 | fn test_911() { 82 | assert_eq!("CMXI", Roman::from(911).to_string()); 83 | } 84 | 85 | #[test] 86 | fn test_1024() { 87 | assert_eq!("MXXIV", Roman::from(1024).to_string()); 88 | } 89 | 90 | #[test] 91 | fn test_3000() { 92 | assert_eq!("MMM", Roman::from(3000).to_string()); 93 | } 94 | -------------------------------------------------------------------------------- /rotational-cipher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rotational-cipher" 3 | version = "1.0.0" 4 | authors = ["Chad Baxter "] 5 | -------------------------------------------------------------------------------- /rotational-cipher/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn rotate(text: &str, number: u8) -> String { 2 | text.chars() 3 | .map(|ch| rotate_char(ch, number)) 4 | .collect::() 5 | } 6 | 7 | fn rotate_char(chr: char, number: u8) -> char { 8 | let (upper_a, a, c) = ('A' as u8, 'a' as u8, chr as u8); 9 | 10 | match chr { 11 | 'A'...'Z' => (((c - upper_a + number) % 26) + upper_a) as char, 12 | 'a'...'z' => (((c - a + number) % 26) + a) as char, 13 | _ => chr, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rotational-cipher/tests/rotational-cipher.rs: -------------------------------------------------------------------------------- 1 | extern crate rotational_cipher as cipher; 2 | 3 | #[test] 4 | fn rotate_a_1() { 5 | assert_eq!("b", cipher::rotate("a", 1)); 6 | } 7 | 8 | #[test] 9 | fn rotate_a_26() { 10 | assert_eq!("a", cipher::rotate("a", 26)); 11 | } 12 | 13 | #[test] 14 | fn rotate_a_0() { 15 | assert_eq!("a", cipher::rotate("a", 0)); 16 | } 17 | 18 | #[test] 19 | fn rotate_m_13() { 20 | assert_eq!("z", cipher::rotate("m", 13)); 21 | } 22 | 23 | #[test] 24 | fn rotate_n_13_with_wrap() { 25 | assert_eq!("a", cipher::rotate("n", 13)); 26 | } 27 | 28 | #[test] 29 | fn rotate_caps() { 30 | assert_eq!("TRL", cipher::rotate("OMG", 5)); 31 | } 32 | 33 | #[test] 34 | fn rotate_spaces() { 35 | assert_eq!("T R L", cipher::rotate("O M G", 5)); 36 | } 37 | 38 | #[test] 39 | fn rotate_numbers() { 40 | assert_eq!("Xiwxmrk 1 2 3 xiwxmrk", cipher::rotate("Testing 1 2 3 testing", 4)); 41 | } 42 | 43 | #[test] 44 | fn rotate_punctuation() { 45 | assert_eq!("Gzo\'n zvo, Bmviyhv!", cipher::rotate("Let\'s eat, Grandma!", 21)); 46 | } 47 | 48 | #[test] 49 | fn rotate_all_the_letters() { 50 | assert_eq!("Gur dhvpx oebja sbk whzcf bire gur ynml qbt.", 51 | cipher::rotate("The quick brown fox jumps over the lazy dog.", 13)); 52 | } 53 | -------------------------------------------------------------------------------- /run-length-encoding/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "run-length-encoding" 3 | version = "0.1.0" 4 | 5 | -------------------------------------------------------------------------------- /run-length-encoding/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "run-length-encoding" 3 | version = "0.1.0" 4 | authors = ["zombiefungus "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /run-length-encoding/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn encode(text: &str) -> String { 2 | if text.is_empty() { 3 | return text.into(); 4 | } 5 | 6 | let mut encoded = String::new(); 7 | let mut count = 1; 8 | let mut current = text.chars().nth(0).unwrap(); 9 | 10 | for c in text.chars().skip(1) { 11 | if c == current { 12 | count += 1; 13 | } else { 14 | encoded.push_str(&convert(count)); 15 | encoded.push(current); 16 | current = c; 17 | count = 1; 18 | } 19 | } 20 | encoded.push_str(&convert(count)); 21 | encoded.push(current); 22 | 23 | encoded 24 | } 25 | 26 | fn convert(n: u32) -> String { 27 | match n { 28 | 1 => "".into(), 29 | _ => n.to_string(), 30 | } 31 | } 32 | 33 | pub fn decode(text: &str) -> String { 34 | let mut numerals = String::new(); 35 | let mut decoded = String::new(); 36 | 37 | for c in text.chars() { 38 | if c.is_numeric() { 39 | numerals.push(c); 40 | } else { 41 | let num: usize = numerals.parse().unwrap_or(1); 42 | decoded.push_str(&c.to_string().repeat(num)); 43 | numerals.clear(); 44 | } 45 | } 46 | decoded 47 | } 48 | -------------------------------------------------------------------------------- /run-length-encoding/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate run_length_encoding as rle; 2 | 3 | fn main() { 4 | // let raw: String = rle::encode("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB"); 5 | // println!("{}", raw); 6 | let encoded = "12WB12W3B24WB"; 7 | // let encoded = "12WB"; 8 | let decoded = rle::decode(&encoded); 9 | println!("{}", decoded); 10 | } 11 | -------------------------------------------------------------------------------- /run-length-encoding/tests/run-length-encoding.rs: -------------------------------------------------------------------------------- 1 | extern crate run_length_encoding as rle; 2 | 3 | // encoding tests 4 | 5 | #[test] 6 | fn test_encode_empty_string() { 7 | assert_eq!("", rle::encode("")); 8 | } 9 | 10 | #[test] 11 | fn test_encode_single_characters() { 12 | assert_eq!("XYZ", rle::encode("XYZ")); 13 | } 14 | 15 | #[test] 16 | fn test_encode_string_with_no_single_characters() { 17 | assert_eq!("2A3B4C", rle::encode("AABBBCCCC")); 18 | } 19 | 20 | #[test] 21 | fn test_encode_single_characters_mixed_with_repeated_characters() { 22 | assert_eq!("12WB12W3B24WB", 23 | rle::encode("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB")); 24 | } 25 | 26 | #[test] 27 | fn test_encode_multiple_whitespace_mixed_in_string() { 28 | assert_eq!("2 hs2q q2w2 ", rle::encode(" hsqq qww ")); 29 | } 30 | 31 | #[test] 32 | fn test_encode_lowercase_characters() { 33 | assert_eq!("2a3b4c", rle::encode("aabbbcccc")); 34 | } 35 | 36 | // decoding tests 37 | 38 | #[test] 39 | fn test_decode_empty_string() { 40 | assert_eq!("", rle::decode("")); 41 | } 42 | 43 | #[test] 44 | fn test_decode_single_characters_only() { 45 | assert_eq!("XYZ", rle::decode("XYZ")); 46 | } 47 | 48 | #[test] 49 | fn test_decode_string_with_no_single_characters() { 50 | assert_eq!("AABBBCCCC", rle::decode("2A3B4C")); 51 | } 52 | 53 | #[test] 54 | fn test_decode_single_characters_with_repeated_characters() { 55 | assert_eq!("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB", 56 | rle::decode("12WB12W3B24WB")); 57 | } 58 | 59 | #[test] 60 | fn test_decode_multiple_whitespace_mixed_in_string() { 61 | assert_eq!(" hsqq qww ", rle::decode("2 hs2q q2w2 ")); 62 | } 63 | 64 | #[test] 65 | fn test_decode_lower_case_string() { 66 | assert_eq!("aabbbcccc", rle::decode("2a3b4c")); 67 | } 68 | 69 | // consistency test 70 | 71 | #[test] 72 | fn test_consistency() { 73 | assert_eq!("zzz ZZ zZ", 74 | rle::decode(rle::encode("zzz ZZ zZ").as_str())); 75 | } 76 | -------------------------------------------------------------------------------- /saddle-points/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | **/*.rs.bk 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 8 | Cargo.lock 9 | -------------------------------------------------------------------------------- /saddle-points/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "saddle-points" 3 | version = "1.1.0" 4 | 5 | [dependencies] 6 | -------------------------------------------------------------------------------- /saddle-points/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn find_saddle_points(input: &[Vec]) -> Vec<(usize, usize)> { 2 | fn get_row(n: usize, input: &[Vec]) -> Vec { 3 | input[n].clone() 4 | } 5 | fn get_column(n: usize, input: &[Vec]) -> Vec { 6 | input.iter().map(|row| row[n]).collect() 7 | } 8 | fn is_greater_or_equal(n: u64, vector: &[u64]) -> bool { 9 | vector.iter().all(|&value| value <= n) 10 | } 11 | fn is_smaller_or_equal(n: u64, vector: &[u64]) -> bool { 12 | vector.iter().all(|&value| n <= value) 13 | } 14 | let mut output = vec![]; 15 | for row_index in 0..input.len() { 16 | let row = get_row(row_index, input); 17 | for col_index in 0..input[0].len() { 18 | let column = get_column(col_index, input); 19 | let value = row[col_index]; 20 | if is_greater_or_equal(value, &row) && is_smaller_or_equal(value, &column) { 21 | output.push((row_index, col_index)); 22 | } 23 | } 24 | } 25 | output 26 | } 27 | -------------------------------------------------------------------------------- /saddle-points/tests/saddle-points.rs: -------------------------------------------------------------------------------- 1 | extern crate saddle_points; 2 | 3 | use saddle_points::*; 4 | 5 | #[test] 6 | fn test_identify_single_saddle_point() { 7 | let input = vec![vec![9, 8, 7], vec![5, 3, 2], vec![6, 6, 7]]; 8 | assert_eq!(vec![(1, 0)], find_saddle_points(&input)); 9 | } 10 | 11 | #[test] 12 | fn test_identify_empty_matrix() { 13 | let input = vec![vec![], vec![], vec![]]; 14 | let expected: Vec<(usize, usize)> = Vec::new(); 15 | assert_eq!(expected, find_saddle_points(&input)); 16 | } 17 | 18 | #[test] 19 | fn test_identify_lack_of_saddle_point() { 20 | let input = vec![vec![1, 2, 3], vec![3, 1, 2], vec![2, 3, 1]]; 21 | let expected: Vec<(usize, usize)> = Vec::new(); 22 | assert_eq!(expected, find_saddle_points(&input)); 23 | } 24 | 25 | #[test] 26 | fn test_multiple_saddle_point() { 27 | let input = vec![vec![4, 5, 4], vec![3, 5, 5], vec![1, 5, 4]]; 28 | assert_eq!(vec![(0, 1), (1, 1), (2, 1)], find_saddle_points(&input)); 29 | } 30 | 31 | #[test] 32 | fn test_identify_bottom_right_saddle_point() { 33 | let input = vec![vec![8, 7, 9], vec![6, 7, 6], vec![3, 2, 5]]; 34 | assert_eq!(vec![(2, 2)], find_saddle_points(&input)); 35 | } 36 | 37 | #[test] 38 | fn test_non_square_matrix_high() { 39 | let input = vec![vec![1, 5], vec![3, 6], vec![2, 7], vec![3, 8]]; 40 | assert_eq!(vec![(0, 1)], find_saddle_points(&input)); 41 | } 42 | 43 | #[test] 44 | fn test_non_square_matrix_wide() { 45 | let input = vec![vec![8, 7, 10, 7, 9], vec![8, 7, 13, 7, 9]]; 46 | assert_eq!(vec![(0, 2)], find_saddle_points(&input)); 47 | } 48 | 49 | #[test] 50 | fn test_vector_matrix() { 51 | let input = vec![vec![1], vec![3], vec![2], vec![3]]; 52 | assert_eq!(vec![(0, 0)], find_saddle_points(&input)); 53 | } 54 | -------------------------------------------------------------------------------- /say/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /say/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "say" 3 | version = "1.1.0" 4 | authors = ["sacherjj "] 5 | -------------------------------------------------------------------------------- /scrabble-score/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scrabble-score" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /scrabble-score/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | pub fn score(word: &str) -> u32 { 3 | word.to_lowercase() 4 | .chars() 5 | .map(|letter| get_score(letter)) 6 | .sum() 7 | } 8 | 9 | fn get_score(letter: char) -> u32 { 10 | match letter { 11 | 'a' | 'e' | 'i' | 'o' | 'u' | 'l' | 'n' | 'r' | 's' | 't' => 1, 12 | 'd' | 'g' => 2, 13 | 'b' | 'c' | 'm' | 'p' => 3, 14 | 'f' | 'h' | 'v' | 'w' | 'y' => 4, 15 | 'k' => 5, 16 | 'j' | 'x' => 8, 17 | 'q' | 'z' => 10, 18 | _ => 0, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /scrabble-score/tests/scrabble-score.rs: -------------------------------------------------------------------------------- 1 | extern crate scrabble_score; 2 | 3 | use scrabble_score::*; 4 | 5 | #[test] 6 | fn a_is_worth_one_point() { 7 | assert_eq!(score("a"), 1); 8 | } 9 | 10 | #[test] 11 | fn scoring_is_case_insensitive() { 12 | assert_eq!(score("A"), 1); 13 | } 14 | 15 | #[test] 16 | fn f_is_worth_four() { 17 | assert_eq!(score("f"), 4); 18 | } 19 | 20 | #[test] 21 | fn two_one_point_letters_make_a_two_point_word() { 22 | assert_eq!(score("at"), 2); 23 | } 24 | 25 | #[test] 26 | fn three_letter_word() { 27 | assert_eq!(score("zoo"), 12); 28 | } 29 | 30 | #[test] 31 | fn medium_word() { 32 | assert_eq!(score("street"), 6); 33 | } 34 | 35 | #[test] 36 | fn longer_words_with_valuable_letters() { 37 | assert_eq!(score("quirky"), 22); 38 | } 39 | 40 | #[test] 41 | fn long_mixed_case_word() { 42 | assert_eq!(score("OxyphenButazone"), 41); 43 | } 44 | 45 | #[test] 46 | fn non_english_scrabble_letters_do_not_score() { 47 | assert_eq!(score("pinata"), 8); 48 | assert_eq!(score("piñata"), 7); 49 | } 50 | 51 | #[test] 52 | fn empty_words_are_worth_zero() { 53 | assert_eq!(score(""), 0); 54 | } 55 | 56 | #[test] 57 | fn all_letters_work() { 58 | assert_eq!(score("abcdefghijklmnopqrstuvwxyz"), 87); 59 | } 60 | -------------------------------------------------------------------------------- /series/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Cargo.lock if creating a library 2 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /series/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "series" 3 | version = "0.1.0" 4 | 5 | [dependencies] 6 | -------------------------------------------------------------------------------- /series/README.md: -------------------------------------------------------------------------------- 1 | # Series 2 | 3 | Given a string of digits, output all the contiguous substrings of length `n` in 4 | that string. 5 | 6 | For example, the string "49142" has the following 3-digit series: 7 | 8 | - 491 9 | - 914 10 | - 142 11 | 12 | And the following 4-digit series: 13 | 14 | - 4914 15 | - 9142 16 | 17 | And if you ask for a 6-digit series from a 5-digit string, you deserve 18 | whatever you get. 19 | 20 | Note that these series are only required to occupy *adjacent positions* 21 | in the input; the digits need not be *numerically consecutive*. 22 | 23 | ## Rust Installation 24 | 25 | Refer to the [exercism help page][help-page] for Rust installation and learning 26 | resources. 27 | 28 | ## Writing the Code 29 | 30 | Execute the tests with: 31 | 32 | ```bash 33 | $ cargo test 34 | ``` 35 | 36 | All but the first test have been ignored. After you get the first test to 37 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 38 | to pass again. The test file is located in the `tests` directory. You can 39 | also remove the ignore flag from all the tests to get them to run all at once 40 | if you wish. 41 | 42 | Make sure to read the [Modules](https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html) chapter if you 43 | haven't already, it will help you with organizing your files. 44 | 45 | ## Feedback, Issues, Pull Requests 46 | 47 | The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help! 48 | 49 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md). 50 | 51 | [help-page]: http://exercism.io/languages/rust 52 | [modules]: https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html 53 | [cargo]: https://doc.rust-lang.org/book/second-edition/ch14-00-more-about-cargo.html 54 | 55 | ## Source 56 | 57 | A subset of the Problem 8 at Project Euler [http://projecteuler.net/problem=8](http://projecteuler.net/problem=8) 58 | 59 | ## Submitting Incomplete Solutions 60 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 61 | -------------------------------------------------------------------------------- /series/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn series(digits: &str, len: usize) -> Vec { 2 | let mut output = vec![]; 3 | if digits.len() < len { 4 | return output; 5 | } 6 | 7 | for start in 0..=(digits.len() - len) { 8 | let substr: &str = &digits[start..(start + len)]; 9 | output.push(substr.to_string()); 10 | } 11 | output 12 | } 13 | -------------------------------------------------------------------------------- /series/tests/series.rs: -------------------------------------------------------------------------------- 1 | extern crate series; 2 | use series::*; 3 | 4 | #[test] 5 | fn test_with_zero_length() { 6 | let expected = vec!["".to_string(); 6]; 7 | assert_eq!(series("92017", 0), expected); 8 | } 9 | 10 | #[test] 11 | fn test_with_length_2() { 12 | let expected = vec![ 13 | "92".to_string(), 14 | "20".to_string(), 15 | "01".to_string(), 16 | "17".to_string(), 17 | ]; 18 | assert_eq!(series("92017", 2), expected); 19 | } 20 | 21 | #[test] 22 | fn test_with_numbers_length() { 23 | let expected = vec!["92017".to_string()]; 24 | assert_eq!(series("92017", 5), expected); 25 | } 26 | 27 | #[test] 28 | fn test_too_long() { 29 | let expected: Vec = vec![]; 30 | assert_eq!(series("92017", 6), expected); 31 | } 32 | -------------------------------------------------------------------------------- /sieve/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sieve" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /sieve/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn primes_up_to(limit: usize) -> Vec { 2 | let mut primes: Vec = (1..=limit).collect(); 3 | primes[0] = 0; 4 | 5 | for value in series(limit) { 6 | for i in range_step(value + 1, limit, value) { 7 | primes[i - 1] = 0; 8 | } 9 | } 10 | primes.into_iter().filter(|&x| x > 0).collect() 11 | } 12 | 13 | fn series(limit: usize) -> Vec { 14 | (2..).take_while(|x| x * x <= limit).collect() 15 | } 16 | 17 | fn range_step(start: usize, end: usize, step: usize) -> Vec { 18 | (start..=end).filter(|x| x % step == 0).collect() 19 | } 20 | -------------------------------------------------------------------------------- /sieve/tests/sieve.rs: -------------------------------------------------------------------------------- 1 | extern crate sieve; 2 | 3 | #[test] 4 | fn limit_lower_than_the_first_prime() { 5 | assert_eq!(sieve::primes_up_to(1), []); 6 | } 7 | 8 | #[test] 9 | fn limit_is_the_first_prime() { 10 | assert_eq!(sieve::primes_up_to(2), [2]); 11 | } 12 | 13 | #[test] 14 | fn primes_up_to_10() { 15 | assert_eq!(sieve::primes_up_to(10), [2, 3, 5, 7]); 16 | } 17 | 18 | 19 | #[test] 20 | fn limit_is_prime() { 21 | assert_eq!(sieve::primes_up_to(13), [2, 3, 5, 7, 11, 13]); 22 | } 23 | 24 | #[test] 25 | fn limit_of_1000() { 26 | let expected = vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 27 | 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 28 | 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 29 | 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 30 | 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 31 | 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 32 | 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 33 | 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 34 | 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 35 | 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 36 | 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]; 37 | assert_eq!(sieve::primes_up_to(1000), expected); 38 | } 39 | -------------------------------------------------------------------------------- /simple-cipher/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Cargo.lock if creating a library 2 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /simple-cipher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple-cipher" 3 | version = "0.0.0" 4 | 5 | [dependencies] 6 | rand = "0.5.0" 7 | -------------------------------------------------------------------------------- /simple-cipher/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate rand; 2 | 3 | use rand::prelude::*; 4 | 5 | fn rotate_char(chr: char, number: u8) -> char { 6 | let (a, c) = (b'a', chr as u8); 7 | 8 | match chr { 9 | 'a'...'z' => (((c - a + number) % 26) + a) as char, 10 | _ => chr, 11 | } 12 | } 13 | 14 | fn calc_distance(chr: char) -> u8 { 15 | chr as u8 - b'a' 16 | } 17 | 18 | fn is_valid(text: &str) -> bool { 19 | !text.is_empty() 20 | && text.chars().all(|chr| chr.is_alphabetic()) 21 | && text.to_ascii_lowercase() == text 22 | } 23 | 24 | fn generate_random_key(length: usize) -> String { 25 | let alphabet: Vec<_> = (b'a'..=b'z').collect(); 26 | let letters = alphabet.as_slice(); 27 | let mut rng = thread_rng(); 28 | let random_key: String = (0..length) 29 | .map(|_| letters[rng.gen_range(0, 26)] as char) 30 | .collect(); 31 | random_key 32 | } 33 | 34 | pub fn encode(key: &str, s: &str) -> Option { 35 | if !is_valid(key) || !is_valid(s) { 36 | return None; 37 | } 38 | let res = s 39 | .chars() 40 | .zip(key.chars().cycle()) 41 | .map(|(chr, key)| rotate_char(chr, calc_distance(key))) 42 | .collect(); 43 | Some(res) 44 | } 45 | 46 | pub fn decode(key: &str, s: &str) -> Option { 47 | if !is_valid(key) || !is_valid(s) { 48 | return None; 49 | } 50 | let res = s 51 | .chars() 52 | .zip(key.chars().cycle()) 53 | .map(|(chr, key)| rotate_char(chr, 26 - calc_distance(key))) 54 | .collect(); 55 | Some(res) 56 | } 57 | 58 | pub fn encode_random(s: &str) -> (String, String) { 59 | let random_key = generate_random_key(128); 60 | match encode(&random_key, s) { 61 | Some(encoded) => (random_key, encoded), 62 | None => (random_key, "".into()), 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /simple-linked-list/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /simple-linked-list/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple_linked_list" 3 | version = "0.1.0" 4 | 5 | [dependencies] 6 | -------------------------------------------------------------------------------- /simple-linked-list/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | struct Node { 3 | data: T, 4 | next: Option>>, 5 | } 6 | 7 | #[derive(Clone)] 8 | pub struct SimpleLinkedList { 9 | head: Option>>, 10 | } 11 | 12 | impl Default for SimpleLinkedList { 13 | fn default() -> Self { 14 | SimpleLinkedList { head: None } 15 | } 16 | } 17 | 18 | impl SimpleLinkedList 19 | where 20 | T: Clone, 21 | { 22 | pub fn new() -> Self { 23 | SimpleLinkedList { head: None } 24 | } 25 | 26 | pub fn len(&self) -> usize { 27 | match self.head { 28 | None => 0, 29 | _ => { 30 | let mut count = 0; 31 | let mut head = &self.head; 32 | while let Some(node) = head { 33 | head = &node.next; 34 | count += 1; 35 | } 36 | count 37 | } 38 | } 39 | } 40 | pub fn is_empty(&self) -> bool { 41 | self.len() == 0 42 | } 43 | pub fn push(&mut self, element: T) { 44 | let new_node = Some(Box::new(Node { 45 | data: element, 46 | next: self.head.take(), 47 | })); 48 | 49 | self.head = new_node; 50 | } 51 | 52 | pub fn pop(&mut self) -> Option { 53 | self.head.take().map(|node| { 54 | let node = *node; 55 | self.head = node.next; 56 | node.data 57 | }) 58 | } 59 | 60 | pub fn peek(&self) -> Option<&T> { 61 | self.head.as_ref().map(|node| &node.data) 62 | } 63 | } 64 | 65 | impl SimpleLinkedList { 66 | pub fn rev(self) -> SimpleLinkedList { 67 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 68 | let mut cloned_list = self.clone(); 69 | 70 | while let Some(value) = cloned_list.pop() { 71 | list.push(value); 72 | } 73 | list 74 | } 75 | } 76 | 77 | impl<'a, T: Clone> From<&'a [T]> for SimpleLinkedList { 78 | fn from(item: &[T]) -> Self { 79 | let mut list = SimpleLinkedList::new(); 80 | for value in item.iter().cloned() { 81 | list.push(value); 82 | } 83 | list 84 | } 85 | } 86 | 87 | impl Into> for SimpleLinkedList { 88 | fn into(self) -> Vec { 89 | let mut vec: Vec = vec![]; 90 | let mut cloned_list = self.clone(); 91 | 92 | while let Some(value) = cloned_list.pop() { 93 | vec.push(value); 94 | } 95 | vec.reverse(); 96 | vec 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /space-age/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /space-age/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "space-age" 3 | version = "1.1.0" 4 | -------------------------------------------------------------------------------- /space-age/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct Duration { 3 | earth_orbits: f64, 4 | } 5 | 6 | impl From for Duration { 7 | fn from(s: u64) -> Self { 8 | let earth_orbits = s as f64 / 31_557_600.0; 9 | Duration { earth_orbits } 10 | } 11 | } 12 | 13 | pub trait Planet { 14 | fn years_during(d: &Duration) -> f64; 15 | } 16 | 17 | pub struct Mercury; 18 | pub struct Venus; 19 | pub struct Earth; 20 | pub struct Mars; 21 | pub struct Jupiter; 22 | pub struct Saturn; 23 | pub struct Uranus; 24 | pub struct Neptune; 25 | 26 | impl Planet for Mercury { 27 | fn years_during(d: &Duration) -> f64 { 28 | d.earth_orbits / 0.240_846_7 29 | } 30 | } 31 | impl Planet for Venus { 32 | fn years_during(d: &Duration) -> f64 { 33 | d.earth_orbits / 0.615_197_26 34 | } 35 | } 36 | 37 | impl Planet for Earth { 38 | fn years_during(d: &Duration) -> f64 { 39 | d.earth_orbits 40 | } 41 | } 42 | impl Planet for Mars { 43 | fn years_during(d: &Duration) -> f64 { 44 | d.earth_orbits / 1.880_815_8 45 | } 46 | } 47 | impl Planet for Jupiter { 48 | fn years_during(d: &Duration) -> f64 { 49 | d.earth_orbits / 11.862_615 50 | } 51 | } 52 | impl Planet for Saturn { 53 | fn years_during(d: &Duration) -> f64 { 54 | d.earth_orbits / 29.447_498 55 | } 56 | } 57 | impl Planet for Uranus { 58 | fn years_during(d: &Duration) -> f64 { 59 | d.earth_orbits / 84.016_846 60 | } 61 | } 62 | impl Planet for Neptune { 63 | fn years_during(d: &Duration) -> f64 { 64 | d.earth_orbits / 164.791_32 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /space-age/tests/space-age.rs: -------------------------------------------------------------------------------- 1 | extern crate space_age; 2 | 3 | use space_age::*; 4 | 5 | fn assert_in_delta(expected: f64, actual: f64) { 6 | let diff: f64 = (expected - actual).abs(); 7 | let delta: f64 = 0.01; 8 | if diff > delta { 9 | panic!( 10 | "Your result of {} should be within {} of the expected result {}", 11 | actual, delta, expected 12 | ) 13 | } 14 | } 15 | 16 | #[test] 17 | fn earth_age() { 18 | let duration = Duration::from(1_000_000_000); 19 | assert_in_delta(31.69, Earth::years_during(&duration)); 20 | } 21 | 22 | #[test] 23 | fn mercury_age() { 24 | let duration = Duration::from(2_134_835_688); 25 | assert_in_delta(280.88, Mercury::years_during(&duration)); 26 | } 27 | 28 | #[test] 29 | fn venus_age() { 30 | let duration = Duration::from(189_839_836); 31 | assert_in_delta(9.78, Venus::years_during(&duration)); 32 | } 33 | 34 | #[test] 35 | fn mars_age() { 36 | let duration = Duration::from(2_329_871_239); 37 | assert_in_delta(39.25, Mars::years_during(&duration)); 38 | } 39 | 40 | #[test] 41 | fn jupiter_age() { 42 | let duration = Duration::from(901_876_382); 43 | assert_in_delta(2.41, Jupiter::years_during(&duration)); 44 | } 45 | 46 | #[test] 47 | fn saturn_age() { 48 | let duration = Duration::from(3_000_000_000); 49 | assert_in_delta(3.23, Saturn::years_during(&duration)); 50 | } 51 | 52 | #[test] 53 | fn uranus_age() { 54 | let duration = Duration::from(3_210_123_456); 55 | assert_in_delta(1.21, Uranus::years_during(&duration)); 56 | } 57 | 58 | #[test] 59 | fn neptune_age() { 60 | let duration = Duration::from(8_210_123_456); 61 | assert_in_delta(1.58, Neptune::years_during(&duration)); 62 | } 63 | -------------------------------------------------------------------------------- /spiral-matrix/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense para saber los atributos posibles. 3 | // Mantenga el puntero para ver las descripciones de los existentes atributos 4 | // Para más información, visite: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug tests in spiral-matrix", 11 | "cargo": { 12 | "args": [ 13 | "test", 14 | "--no-run", 15 | "--lib" 16 | ] 17 | }, 18 | "args": [], 19 | "cwd": "${workspaceFolder}" 20 | }, 21 | { 22 | "type": "lldb", 23 | "request": "launch", 24 | "name": "Debug spiral-matrix", 25 | "cargo": { 26 | "args": [ 27 | "build", 28 | "--bin=spiral-matrix" 29 | ] 30 | }, 31 | "args": [], 32 | "cwd": "${workspaceFolder}" 33 | }, 34 | { 35 | "type": "lldb", 36 | "request": "launch", 37 | "name": "Debug tests in spiral-matrix", 38 | "cargo": { 39 | "args": [ 40 | "test", 41 | "--no-run", 42 | "--bin=spiral-matrix" 43 | ] 44 | }, 45 | "args": [], 46 | "cwd": "${workspaceFolder}" 47 | }, 48 | { 49 | "type": "lldb", 50 | "request": "launch", 51 | "name": "Debug spiral-matrix", 52 | "cargo": { 53 | "args": [ 54 | "build", 55 | "--test=spiral-matrix" 56 | ] 57 | }, 58 | "args": [], 59 | "cwd": "${workspaceFolder}" 60 | }, 61 | { 62 | "type": "lldb", 63 | "request": "launch", 64 | "name": "Debug tests in spiral-matrix", 65 | "cargo": { 66 | "args": [ 67 | "test", 68 | "--no-run", 69 | "--test=spiral-matrix" 70 | ] 71 | }, 72 | "args": [], 73 | "cwd": "${workspaceFolder}" 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /spiral-matrix/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spiral-matrix" 3 | version = "1.1.0" 4 | 5 | -------------------------------------------------------------------------------- /spiral-matrix/tests/spiral-matrix.rs: -------------------------------------------------------------------------------- 1 | extern crate spiral_matrix; 2 | use spiral_matrix::*; 3 | 4 | #[test] 5 | fn empty_spiral() { 6 | let expected: Vec> = Vec::new(); 7 | assert_eq!(spiral_matrix(0), expected); 8 | } 9 | 10 | #[test] 11 | fn size_one_spiral() { 12 | let mut expected: Vec> = Vec::new(); 13 | expected.push(vec![1]); 14 | assert_eq!(spiral_matrix(1), expected); 15 | } 16 | #[test] 17 | fn size_two_spiral() { 18 | let mut expected: Vec> = Vec::new(); 19 | expected.push(vec![1, 2]); 20 | expected.push(vec![4, 3]); 21 | assert_eq!(spiral_matrix(2), expected); 22 | } 23 | 24 | #[test] 25 | fn size_three_spiral() { 26 | let mut expected: Vec> = Vec::new(); 27 | expected.push(vec![1, 2, 3]); 28 | expected.push(vec![8, 9, 4]); 29 | expected.push(vec![7, 6, 5]); 30 | assert_eq!(spiral_matrix(3), expected); 31 | } 32 | #[test] 33 | fn size_four_spiral() { 34 | let mut expected: Vec> = Vec::new(); 35 | expected.push(vec![1, 2, 3, 4]); 36 | expected.push(vec![12, 13, 14, 5]); 37 | expected.push(vec![11, 16, 15, 6]); 38 | expected.push(vec![10, 9, 8, 7]); 39 | assert_eq!(spiral_matrix(4), expected); 40 | } 41 | #[test] 42 | fn size_five_spiral() { 43 | let mut expected: Vec> = Vec::new(); 44 | expected.push(vec![1, 2, 3, 4, 5]); 45 | expected.push(vec![16, 17, 18, 19, 6]); 46 | expected.push(vec![15, 24, 25, 20, 7]); 47 | expected.push(vec![14, 23, 22, 21, 8]); 48 | expected.push(vec![13, 12, 11, 10, 9]); 49 | assert_eq!(spiral_matrix(5), expected); 50 | } 51 | -------------------------------------------------------------------------------- /sublist/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /sublist/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sublist" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /sublist/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Comparison { 3 | Equal, 4 | Unequal, 5 | Sublist, 6 | Superlist, 7 | } 8 | 9 | use Comparison::*; 10 | 11 | fn is_infix_of(first: &[T], second: &[T]) -> bool { 12 | if first.is_empty() { 13 | return true; 14 | } 15 | if second.is_empty() { 16 | return false; 17 | } 18 | 19 | let clipped: Vec = second 20 | .iter() 21 | .cloned() 22 | .skip_while(|value| *value != first[0]) 23 | .collect(); 24 | 25 | if clipped.is_empty() || first.len() > clipped.len() { 26 | return false; 27 | } 28 | 29 | let mut zipped = first.iter().zip(clipped.iter()); 30 | if !zipped.all(|(a, b)| a == b) { 31 | return is_infix_of(first, &clipped[1..]); 32 | } 33 | true 34 | } 35 | 36 | pub fn sublist(first: &[T], second: &[T]) -> Comparison { 37 | if first == second { 38 | return Equal; 39 | } else if is_infix_of(first, second) { 40 | return Sublist; 41 | } else if is_infix_of(second, first) { 42 | return Superlist; 43 | } else { 44 | return Unequal; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sum-of-multiples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sum-of-multiples" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /sum-of-multiples/README.md: -------------------------------------------------------------------------------- 1 | # Sum Of Multiples 2 | 3 | Given a number, find the sum of all the multiples of particular numbers up to but not including that number. 4 | 5 | If we list all the natural numbers up to but not including 20 that are 6 | multiples of either 3 or 5, we get 3, 5, 6 and 9, 10, 12, 15, and 18. 7 | 8 | The sum of these multiples is 78. 9 | 10 | Given a number, find the sum of the multiples of a given set of numbers, 11 | up to but not including that number. 12 | 13 | ## Rust Installation 14 | 15 | Refer to the [exercism help page][help-page] for Rust installation and learning 16 | resources. 17 | 18 | ## Writing the Code 19 | 20 | Execute the tests with: 21 | 22 | ```bash 23 | $ cargo test 24 | ``` 25 | 26 | All but the first test have been ignored. After you get the first test to 27 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 28 | to pass again. The test file is located in the `tests` directory. You can 29 | also remove the ignore flag from all the tests to get them to run all at once 30 | if you wish. 31 | 32 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 33 | haven't already, it will help you with organizing your files. 34 | 35 | ## Feedback, Issues, Pull Requests 36 | 37 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 38 | 39 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 40 | 41 | [help-page]: http://exercism.io/languages/rust 42 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 43 | 44 | ## Source 45 | 46 | A variation on Problem 1 at Project Euler [http://projecteuler.net/problem=1](http://projecteuler.net/problem=1) 47 | 48 | ## Submitting Incomplete Problems 49 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 50 | 51 | -------------------------------------------------------------------------------- /sum-of-multiples/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | pub fn sum_of_multiples(limit : usize, factors: &Vec) -> usize { 3 | let mut result = 0; 4 | 5 | for x in 1..limit { 6 | for y in factors { 7 | if x % y == 0 { 8 | result += x; 9 | break; 10 | } 11 | } 12 | } 13 | result 14 | } 15 | -------------------------------------------------------------------------------- /sum-of-multiples/tests/sum-of-multiples.rs: -------------------------------------------------------------------------------- 1 | extern crate sum_of_multiples; 2 | 3 | use sum_of_multiples::*; 4 | 5 | #[test] 6 | fn multiples_one() { 7 | assert_eq!(0, sum_of_multiples(1, &vec![3, 5])) 8 | } 9 | 10 | #[test] 11 | fn multiples_two() { 12 | assert_eq!(3, sum_of_multiples(4, &vec![3, 5])) 13 | } 14 | 15 | #[test] 16 | fn multiples_three() { 17 | assert_eq!(23, sum_of_multiples(10, &vec![3, 5])) 18 | } 19 | 20 | #[test] 21 | fn multiples_four() { 22 | assert_eq!(2318, sum_of_multiples(100, &vec![3, 5])) 23 | } 24 | 25 | #[test] 26 | fn multiples_five() { 27 | assert_eq!(233168, sum_of_multiples(1000, &vec![3, 5])) 28 | } 29 | 30 | #[test] 31 | fn multiples_six() { 32 | assert_eq!(51, sum_of_multiples(20, &vec![7, 13, 17])) 33 | } 34 | 35 | #[test] 36 | fn multiples_seven() { 37 | assert_eq!(30, sum_of_multiples(15, &vec![4, 6])) 38 | } 39 | 40 | #[test] 41 | fn multiples_eight() { 42 | assert_eq!(4419, sum_of_multiples(150, &vec![5, 6, 8])) 43 | } 44 | 45 | #[test] 46 | fn multiples_nine() { 47 | assert_eq!(275, sum_of_multiples(51, &vec![5, 25])) 48 | } 49 | 50 | #[test] 51 | fn multiples_ten() { 52 | assert_eq!(2203160, sum_of_multiples(10000, &vec![43, 47])) 53 | } 54 | 55 | #[test] 56 | fn multiples_eleven() { 57 | assert_eq!(4950, sum_of_multiples(100, &vec![1])) 58 | } 59 | 60 | #[test] 61 | fn multiples_twelve() { 62 | assert_eq!(0, sum_of_multiples(10000, &vec![])) 63 | } 64 | -------------------------------------------------------------------------------- /tournament/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /tournament/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tournament" 3 | version = "1.4.0" 4 | -------------------------------------------------------------------------------- /triangle/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "triangle" 3 | version = "0.0.0" 4 | 5 | [dependencies] 6 | num = "0.1.37" -------------------------------------------------------------------------------- /triangle/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate num; 2 | use num::Num; 3 | use num::Zero; 4 | 5 | use std::cmp::Ordering; 6 | 7 | #[derive(Debug, PartialEq)] 8 | pub enum TriangleType { 9 | Equilateral, 10 | Isosceles, 11 | Scalene, 12 | } 13 | 14 | #[derive(Debug)] 15 | pub struct Triangle { 16 | a: T, 17 | b: T, 18 | c: T, 19 | kind: TriangleType, 20 | } 21 | 22 | impl Triangle { 23 | pub fn build(mut sides: [T; 3]) -> Result { 24 | sides.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less)); 25 | let sides = (sides[0], sides[1], sides[2]); 26 | match sides { 27 | (a, b, c) if Zero::is_zero(&a) || Zero::is_zero(&b) || Zero::is_zero(&c) => { 28 | Err("Invalid triangle".into()) 29 | } 30 | (a, b, c) if a + b < c => Err("Invalid triangle".into()), 31 | (a, b, c) if a == b && b == c => Ok(Self::construct(sides, TriangleType::Equilateral)), 32 | (a, b, c) if a == b || b == c || a == c => { 33 | Ok(Self::construct(sides, TriangleType::Isosceles)) 34 | } 35 | sides => Ok(Self::construct(sides, TriangleType::Scalene)), 36 | } 37 | } 38 | pub fn is_equilateral(&self) -> bool { 39 | self.kind == TriangleType::Equilateral 40 | } 41 | 42 | pub fn is_isosceles(&self) -> bool { 43 | self.kind == TriangleType::Isosceles 44 | } 45 | 46 | pub fn is_scalene(&self) -> bool { 47 | self.kind == TriangleType::Scalene 48 | } 49 | 50 | fn construct(sides: (T, T, T), kind: TriangleType) -> Self { 51 | Triangle { 52 | a: sides.0, 53 | b: sides.1, 54 | c: sides.2, 55 | kind: kind, 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /two-bucket/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "two-bucket" 3 | version = "1.0.0" 4 | -------------------------------------------------------------------------------- /two-bucket/src/bucket.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug, PartialEq)] 2 | pub struct Bucket { 3 | pub contents: u8, 4 | pub capacity: u8, 5 | } 6 | 7 | impl Bucket { 8 | pub fn new(capacity: u8) -> Self { 9 | Bucket { 10 | contents: 0, 11 | capacity, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /two-bucket/tests/two-bucket.rs: -------------------------------------------------------------------------------- 1 | extern crate two_bucket; 2 | 3 | use two_bucket::{solve, Bucket, BucketStats}; 4 | 5 | #[test] 6 | fn test_case_1() { 7 | assert_eq!( 8 | solve(3, 5, 1, &Bucket::One), 9 | BucketStats { 10 | moves: 4, 11 | goal_bucket: Bucket::One, 12 | other_bucket: 5, 13 | } 14 | ); 15 | } 16 | 17 | #[test] 18 | fn test_case_2() { 19 | assert_eq!( 20 | solve(3, 5, 1, &Bucket::Two), 21 | BucketStats { 22 | moves: 8, 23 | goal_bucket: Bucket::Two, 24 | other_bucket: 3, 25 | } 26 | ); 27 | } 28 | 29 | #[test] 30 | fn test_case_3() { 31 | assert_eq!( 32 | solve(7, 11, 2, &Bucket::One), 33 | BucketStats { 34 | moves: 14, 35 | goal_bucket: Bucket::One, 36 | other_bucket: 11, 37 | } 38 | ); 39 | } 40 | 41 | #[test] 42 | fn test_case_4() { 43 | assert_eq!( 44 | solve(7, 11, 2, &Bucket::Two), 45 | BucketStats { 46 | moves: 18, 47 | goal_bucket: Bucket::Two, 48 | other_bucket: 7, 49 | } 50 | ); 51 | } 52 | 53 | #[test] 54 | fn test_case_5() { 55 | assert_eq!( 56 | solve(1, 3, 3, &Bucket::Two), 57 | BucketStats { 58 | moves: 1, 59 | goal_bucket: Bucket::Two, 60 | other_bucket: 0, 61 | } 62 | ); 63 | } 64 | 65 | #[test] 66 | fn test_case_6() { 67 | assert_eq!( 68 | solve(2, 3, 3, &Bucket::One), 69 | BucketStats { 70 | moves: 2, 71 | goal_bucket: Bucket::Two, 72 | other_bucket: 2, 73 | } 74 | ); 75 | } 76 | -------------------------------------------------------------------------------- /twofer/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /twofer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "twofer" 3 | version = "1.2.0" 4 | 5 | [dependencies] 6 | -------------------------------------------------------------------------------- /twofer/README.md: -------------------------------------------------------------------------------- 1 | ## Two-fer 2 | 3 | `Two-fer` or `2-fer` is short for two for one. One for you and one for me. 4 | 5 | ```text 6 | "One for X, one for me." 7 | ``` 8 | 9 | When X is a name or "you". 10 | 11 | If the given name is "Alice", the result should be "One for Alice, one for me." 12 | If no name is given, the result should be "One for you, one for me." 13 | 14 | ## Rust Installation 15 | 16 | Refer to the [exercism help page][help-page] for Rust installation and learning 17 | resources. 18 | 19 | ## Writing the Code 20 | 21 | Execute the tests with: 22 | 23 | ```bash 24 | $ cargo test 25 | ``` 26 | 27 | All but the first test have been ignored. After you get the first test to 28 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 29 | to pass again. The test file is located in the `tests` directory. You can 30 | also remove the ignore flag from all the tests to get them to run all at once 31 | if you wish. 32 | 33 | Make sure to read the [Modules](https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html) chapter if you 34 | haven't already, it will help you with organizing your files. 35 | 36 | ## Feedback, Issues, Pull Requests 37 | 38 | The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help! 39 | 40 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md). 41 | 42 | [help-page]: http://exercism.io/languages/rust 43 | [modules]: https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html 44 | [cargo]: https://doc.rust-lang.org/book/second-edition/ch14-00-more-about-cargo.html 45 | 46 | ## Submitting Incomplete Solutions 47 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. -------------------------------------------------------------------------------- /twofer/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn twofer(name: &str) -> String { 2 | match name { 3 | "" => "One for you, one for me.".into(), 4 | _ => format!("One for {}, one for me.", name), 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /twofer/tests/two-fer.rs: -------------------------------------------------------------------------------- 1 | extern crate twofer; 2 | use twofer::twofer; 3 | 4 | #[test] 5 | fn empty_string() { 6 | assert_eq!(twofer(""), "One for you, one for me."); 7 | } 8 | 9 | #[test] 10 | fn alice() { 11 | assert_eq!(twofer("Alice"), "One for Alice, one for me."); 12 | } 13 | 14 | #[test] 15 | fn bob() { 16 | assert_eq!(twofer("Bob"), "One for Bob, one for me."); 17 | } 18 | -------------------------------------------------------------------------------- /variable-length-quantity/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /variable-length-quantity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "variable-length-quantity" 3 | version = "1.1.0" 4 | -------------------------------------------------------------------------------- /variable-length-quantity/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Error { 3 | IncompleteNumber, 4 | Overflow, 5 | } 6 | 7 | const MAX_FIRST_BLOCK: u8 = 0x8f; 8 | const VLQ_END_MARKER: u8 = 0x7f; 9 | const MAX_BLOCKS: usize = 5; 10 | const U8_BIT_MASK: u8 = 0x7f; 11 | const U32_BIT_MASK: u32 = 0x7f; 12 | const BIT_EIGHT: u8 = 0x80; 13 | const SEVEN_BITS: u32 = 7; 14 | 15 | /// Convert a list of numbers to a stream of bytes encoded with variable length encoding. 16 | pub fn to_bytes(values: &[u32]) -> Vec { 17 | values 18 | .into_iter() 19 | .flat_map(|value| to_bytes_helper(&[*value])) 20 | .collect() 21 | } 22 | 23 | fn to_bytes_helper(values: &[u32]) -> Vec { 24 | let mut num: u32 = values[0]; 25 | if num == 0 { 26 | return vec![0u8]; 27 | } 28 | let mut octets = vec![]; 29 | let mut first_octet = true; 30 | 31 | while num > 0 { 32 | if first_octet { 33 | octets.push(num as u8 & U8_BIT_MASK); 34 | first_octet = false; 35 | } else { 36 | octets.push(num as u8 | BIT_EIGHT); 37 | } 38 | num >>= SEVEN_BITS; 39 | } 40 | octets.reverse(); 41 | octets 42 | } 43 | 44 | /// Given a stream of bytes, extract all numbers which are encoded in there. 45 | pub fn from_bytes(values: &[u8]) -> Result, Error> { 46 | if !values.is_empty() && *values.last().unwrap() > VLQ_END_MARKER { 47 | return Err(Error::IncompleteNumber); 48 | } 49 | 50 | let mut result = vec![]; 51 | let mut blocks = vec![]; 52 | for &value in values { 53 | if value > VLQ_END_MARKER { 54 | blocks.push(value); 55 | } else { 56 | blocks.push(value); 57 | if blocks.len() >= MAX_BLOCKS && *blocks.first().unwrap() > MAX_FIRST_BLOCK { 58 | return Err(Error::Overflow); 59 | } 60 | result.push(from_bytes_helper(&blocks)); 61 | blocks = vec![]; 62 | } 63 | } 64 | let flattened: Vec = result.into_iter().flat_map(|x| x).collect(); 65 | Ok(flattened) 66 | } 67 | 68 | pub fn from_bytes_helper(bytes: &[u8]) -> Vec { 69 | let mut shift_left = 0; 70 | let mut result: u32 = 0; 71 | let mut octets: Vec = bytes.to_vec(); 72 | octets.reverse(); 73 | for octet in octets { 74 | let mut tmp = octet as u32 & U32_BIT_MASK; 75 | tmp <<= shift_left; 76 | shift_left += SEVEN_BITS; 77 | result += tmp; 78 | } 79 | vec![result] 80 | } 81 | -------------------------------------------------------------------------------- /word-count/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "word-count" 3 | version = "0.0.0" 4 | -------------------------------------------------------------------------------- /word-count/README.md: -------------------------------------------------------------------------------- 1 | # Word Count 2 | 3 | Given a phrase, count the occurrences of each word in that phrase. 4 | 5 | For example for the input `"olly olly in come free"` 6 | 7 | ```plain 8 | olly: 2 9 | in: 1 10 | come: 1 11 | free: 1 12 | ``` 13 | 14 | 15 | ## Rust Installation 16 | 17 | Refer to the [exercism help page][help-page] for Rust installation and learning 18 | resources. 19 | 20 | ## Writing the Code 21 | 22 | Execute the tests with: 23 | 24 | ```bash 25 | $ cargo test 26 | ``` 27 | 28 | All but the first test have been ignored. After you get the first test to 29 | pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests 30 | to pass again. The test file is located in the `tests` directory. You can 31 | also remove the ignore flag from all the tests to get them to run all at once 32 | if you wish. 33 | 34 | Make sure to read the [Crates and Modules](https://doc.rust-lang.org/stable/book/crates-and-modules.html) chapter if you 35 | haven't already, it will help you with organizing your files. 36 | 37 | ## Feedback, Issues, Pull Requests 38 | 39 | The [exercism/xrust](https://github.com/exercism/xrust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help! 40 | 41 | If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md). 42 | 43 | [help-page]: http://exercism.io/languages/rust 44 | [crates-and-modules]: http://doc.rust-lang.org/stable/book/crates-and-modules.html 45 | 46 | ## Source 47 | 48 | This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour. 49 | 50 | ## Submitting Incomplete Problems 51 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 52 | 53 | -------------------------------------------------------------------------------- /word-count/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | pub fn word_count(text: &str) -> HashMap { 4 | let cleaned = clean(&text.to_lowercase()); 5 | let mut words = HashMap::new(); 6 | 7 | for word in cleaned.split_terminator(' ') { 8 | let counter = words.entry(word.into()).or_insert(0); 9 | *counter += 1; 10 | } 11 | words 12 | } 13 | 14 | fn clean(text: &str) -> String { 15 | let text = text.chars() 16 | .filter(|&ch| ch.is_alphanumeric() || ch == ' ') 17 | .collect::(); 18 | text.replace(" ", " ") 19 | } 20 | -------------------------------------------------------------------------------- /word-count/tests/word-count.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | extern crate word_count; 4 | 5 | fn check_word_count(s: &str, pairs: Vec<(&str, u32)>) { 6 | // The reason for the awkward code in here is to ensure that the failure 7 | // message for assert_eq! is as informative as possible. A simpler 8 | // solution would simply check the length of the map, and then 9 | // check for the presence and value of each key in the given pairs vector. 10 | let mut m: HashMap = word_count::word_count(s); 11 | for &(k, v) in pairs.iter() { 12 | assert_eq!((k, m.remove(&k.to_string()).unwrap_or(0)), (k, v)); 13 | } 14 | // may fail with a message that clearly shows all extra pairs in the map 15 | assert_eq!(m.iter().collect::>(), vec![]); 16 | } 17 | 18 | #[test] 19 | fn test_count_one_word() { 20 | check_word_count("word", vec![("word", 1)]); 21 | } 22 | 23 | #[test] 24 | fn test_count_one_of_each() { 25 | check_word_count("one of each", vec![("one", 1), ("of", 1), ("each", 1)]); 26 | } 27 | 28 | #[test] 29 | fn test_count_multiple_occurrences() { 30 | check_word_count("one fish two fish red fish blue fish", 31 | vec![("one", 1), ("fish", 4), ("two", 1), ("red", 1), ("blue", 1)]); 32 | } 33 | 34 | #[test] 35 | fn test_ignore_punctuation() { 36 | check_word_count("car : carpet as java : javascript!!&@$%^&", 37 | vec![("car", 1), 38 | ("carpet", 1), 39 | ("as", 1), 40 | ("java", 1), 41 | ("javascript", 1)]); 42 | } 43 | 44 | #[test] 45 | fn test_include_numbers() { 46 | check_word_count("testing, 1, 2 testing", 47 | vec![("testing", 2), ("1", 1), ("2", 1)]); 48 | } 49 | 50 | #[test] 51 | fn test_normalize_case() { 52 | check_word_count("go Go GO Stop stop", vec![("go", 3), ("stop", 2)]); 53 | } 54 | -------------------------------------------------------------------------------- /wordy/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /wordy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wordy" 3 | version = "1.1.0" 4 | --------------------------------------------------------------------------------