From bc97312cc2ded21141323ef322152fc59cad7832 Mon Sep 17 00:00:00 2001 From: "Wojciech S. Gac" Date: Tue, 26 Sep 2023 14:34:14 +0200 Subject: [PATCH] Add some exercises to the Rust path --- rust/.gitignore | 2 + rust/all-your-base/.gitignore | 8 + rust/all-your-base/Cargo.toml | 4 + rust/all-your-base/HELP.md | 85 ++++++++ rust/all-your-base/README.md | 62 ++++++ rust/all-your-base/src/lib.rs | 65 ++++++ rust/all-your-base/tests/all-your-base.rs | 200 ++++++++++++++++++ rust/armstrong-numbers/.gitignore | 8 + rust/armstrong-numbers/Cargo.toml | 4 + rust/armstrong-numbers/HELP.md | 85 ++++++++ rust/armstrong-numbers/README.md | 44 ++++ rust/armstrong-numbers/src/lib.rs | 21 ++ .../tests/armstrong-numbers.rs | 69 ++++++ rust/assembly-line/.gitignore | 8 + rust/assembly-line/Cargo.toml | 4 + rust/assembly-line/HELP.md | 85 ++++++++ rust/assembly-line/HINTS.md | 12 ++ rust/assembly-line/README.md | 88 ++++++++ rust/assembly-line/src/lib.rs | 19 ++ rust/assembly-line/tests/assembly-line.rs | 65 ++++++ rust/difference-of-squares/.gitignore | 8 + rust/difference-of-squares/Cargo.toml | 4 + rust/difference-of-squares/HELP.md | 85 ++++++++ rust/difference-of-squares/README.md | 54 +++++ rust/difference-of-squares/src/lib.rs | 11 + .../tests/difference-of-squares.rs | 46 ++++ rust/etl/.gitignore | 8 + rust/etl/Cargo.toml | 4 + rust/etl/HELP.md | 85 ++++++++ rust/etl/README.md | 82 +++++++ rust/etl/src/lib.rs | 11 + rust/etl/tests/etl.rs | 80 +++++++ rust/gigasecond/.gitignore | 8 + rust/gigasecond/Cargo.toml | 7 + rust/gigasecond/HELP.md | 85 ++++++++ rust/gigasecond/README.md | 76 +++++++ rust/gigasecond/src/lib.rs | 8 + rust/gigasecond/tests/gigasecond.rs | 48 +++++ rust/grains/.gitignore | 8 + rust/grains/Cargo.toml | 4 + rust/grains/HELP.md | 85 ++++++++ rust/grains/README.md | 61 ++++++ rust/grains/src/lib.rs | 10 + rust/grains/tests/grains.rs | 63 ++++++ rust/hamming/.gitignore | 8 + rust/hamming/Cargo.toml | 4 + rust/hamming/HELP.md | 85 ++++++++ rust/hamming/README.md | 59 ++++++ rust/hamming/src/lib.rs | 7 + rust/hamming/tests/hamming.rs | 77 +++++++ rust/isogram/.gitignore | 8 + rust/isogram/Cargo.toml | 4 + rust/isogram/HELP.md | 85 ++++++++ rust/isogram/README.md | 42 ++++ rust/isogram/src/lib.rs | 28 +++ rust/isogram/tests/isogram.rs | 67 ++++++ rust/leap/.gitignore | 8 + rust/leap/Cargo.toml | 4 + rust/leap/HELP.md | 85 ++++++++ rust/leap/README.md | 72 +++++++ rust/leap/src/lib.rs | 3 + rust/leap/tests/leap.rs | 87 ++++++++ rust/lucians-luscious-lasagna/.gitignore | 8 + rust/lucians-luscious-lasagna/Cargo.toml | 4 + rust/lucians-luscious-lasagna/HELP.md | 85 ++++++++ rust/lucians-luscious-lasagna/HINTS.md | 31 +++ rust/lucians-luscious-lasagna/README.md | 159 ++++++++++++++ rust/lucians-luscious-lasagna/src/lib.rs | 19 ++ .../tests/lucians-luscious-lasagna.rs | 39 ++++ rust/luhn/.gitignore | 8 + rust/luhn/Cargo.toml | 4 + rust/luhn/HELP.md | 85 ++++++++ rust/luhn/README.md | 102 +++++++++ rust/luhn/src/lib.rs | 23 ++ rust/luhn/tests/luhn.rs | 121 +++++++++++ rust/macros/.gitignore | 8 + rust/macros/Cargo.toml | 6 + rust/macros/HELP.md | 85 ++++++++ rust/macros/README.md | 66 ++++++ rust/macros/src/lib.rs | 12 ++ rust/macros/tests/invalid/Cargo.toml | 55 +++++ rust/macros/tests/invalid/comma-sep.rs | 7 + rust/macros/tests/invalid/double-commas.rs | 7 + rust/macros/tests/invalid/leading-comma.rs | 7 + rust/macros/tests/invalid/missing-argument.rs | 7 + rust/macros/tests/invalid/no-comma.rs | 7 + rust/macros/tests/invalid/only-arrow.rs | 7 + rust/macros/tests/invalid/only-comma.rs | 7 + rust/macros/tests/invalid/single-argument.rs | 7 + rust/macros/tests/invalid/triple-arguments.rs | 7 + rust/macros/tests/invalid/two-arrows.rs | 7 + rust/macros/tests/macros.rs | 199 +++++++++++++++++ rust/matching-brackets/.gitignore | 8 + rust/matching-brackets/Cargo.toml | 4 + rust/matching-brackets/HELP.md | 85 ++++++++ rust/matching-brackets/README.md | 40 ++++ rust/matching-brackets/src/lib.rs | 21 ++ .../tests/matching-brackets.rs | 98 +++++++++ rust/minesweeper/.gitignore | 8 + rust/minesweeper/Cargo.toml | 4 + rust/minesweeper/HELP.md | 85 ++++++++ rust/minesweeper/README.md | 80 +++++++ rust/minesweeper/src/lib.rs | 68 ++++++ rust/minesweeper/tests/minesweeper.rs | 141 ++++++++++++ rust/nucleotide-count/.gitignore | 8 + rust/nucleotide-count/Cargo.toml | 4 + rust/nucleotide-count/HELP.md | 85 ++++++++ rust/nucleotide-count/README.md | 59 ++++++ rust/nucleotide-count/src/lib.rs | 22 ++ .../tests/nucleotide-count.rs | 88 ++++++++ rust/parallel-letter-frequency/.gitignore | 8 + rust/parallel-letter-frequency/Cargo.toml | 7 + rust/parallel-letter-frequency/HELP.md | 85 ++++++++ rust/parallel-letter-frequency/README.md | 75 +++++++ .../benches/benchmark.rs | 102 +++++++++ rust/parallel-letter-frequency/src/lib.rs | 29 +++ .../tests/parallel-letter-frequency.rs | 122 +++++++++++ rust/raindrops/.gitignore | 8 + rust/raindrops/Cargo.toml | 7 + rust/raindrops/HELP.md | 85 ++++++++ rust/raindrops/README.md | 54 +++++ rust/raindrops/src/lib.rs | 17 ++ rust/raindrops/tests/raindrops.rs | 94 ++++++++ rust/reverse-string/.gitignore | 8 + rust/reverse-string/Cargo.toml | 10 + rust/reverse-string/HELP.md | 85 ++++++++ rust/reverse-string/README.md | 55 +++++ rust/reverse-string/src/lib.rs | 10 + rust/reverse-string/tests/reverse-string.rs | 62 ++++++ rust/robot-simulator/.gitignore | 8 + rust/robot-simulator/Cargo.toml | 7 + rust/robot-simulator/HELP.md | 85 ++++++++ rust/robot-simulator/README.md | 59 ++++++ rust/robot-simulator/src/lib.rs | 83 ++++++++ rust/robot-simulator/tests/robot-simulator.rs | 126 +++++++++++ rust/semi-structured-logs/.gitignore | 8 + rust/semi-structured-logs/Cargo.toml | 9 + rust/semi-structured-logs/HELP.md | 85 ++++++++ rust/semi-structured-logs/HINTS.md | 13 ++ rust/semi-structured-logs/README.md | 64 ++++++ rust/semi-structured-logs/src/lib.rs | 35 +++ .../tests/semi-structured-logs.rs | 47 ++++ rust/space-age/.gitignore | 8 + rust/space-age/Cargo.toml | 4 + rust/space-age/HELP.md | 85 ++++++++ rust/space-age/README.md | 69 ++++++ rust/space-age/src/lib.rs | 48 +++++ rust/space-age/tests/space-age.rs | 57 +++++ rust/sublist/.gitignore | 8 + rust/sublist/Cargo.toml | 4 + rust/sublist/HELP.md | 85 ++++++++ rust/sublist/README.md | 49 +++++ rust/sublist/src/lib.rs | 13 ++ rust/sublist/src/main.rs | 12 ++ rust/sublist/tests/sublist.rs | 127 +++++++++++ 155 files changed, 6830 insertions(+) create mode 100644 rust/.gitignore create mode 100644 rust/all-your-base/.gitignore create mode 100644 rust/all-your-base/Cargo.toml create mode 100644 rust/all-your-base/HELP.md create mode 100644 rust/all-your-base/README.md create mode 100644 rust/all-your-base/src/lib.rs create mode 100644 rust/all-your-base/tests/all-your-base.rs create mode 100644 rust/armstrong-numbers/.gitignore create mode 100644 rust/armstrong-numbers/Cargo.toml create mode 100644 rust/armstrong-numbers/HELP.md create mode 100644 rust/armstrong-numbers/README.md create mode 100644 rust/armstrong-numbers/src/lib.rs create mode 100644 rust/armstrong-numbers/tests/armstrong-numbers.rs create mode 100644 rust/assembly-line/.gitignore create mode 100644 rust/assembly-line/Cargo.toml create mode 100644 rust/assembly-line/HELP.md create mode 100644 rust/assembly-line/HINTS.md create mode 100644 rust/assembly-line/README.md create mode 100644 rust/assembly-line/src/lib.rs create mode 100644 rust/assembly-line/tests/assembly-line.rs create mode 100644 rust/difference-of-squares/.gitignore create mode 100644 rust/difference-of-squares/Cargo.toml create mode 100644 rust/difference-of-squares/HELP.md create mode 100644 rust/difference-of-squares/README.md create mode 100644 rust/difference-of-squares/src/lib.rs create mode 100644 rust/difference-of-squares/tests/difference-of-squares.rs create mode 100644 rust/etl/.gitignore create mode 100644 rust/etl/Cargo.toml create mode 100644 rust/etl/HELP.md create mode 100644 rust/etl/README.md create mode 100644 rust/etl/src/lib.rs create mode 100644 rust/etl/tests/etl.rs create mode 100644 rust/gigasecond/.gitignore create mode 100644 rust/gigasecond/Cargo.toml create mode 100644 rust/gigasecond/HELP.md create mode 100644 rust/gigasecond/README.md create mode 100644 rust/gigasecond/src/lib.rs create mode 100644 rust/gigasecond/tests/gigasecond.rs create mode 100644 rust/grains/.gitignore create mode 100644 rust/grains/Cargo.toml create mode 100644 rust/grains/HELP.md create mode 100644 rust/grains/README.md create mode 100644 rust/grains/src/lib.rs create mode 100644 rust/grains/tests/grains.rs create mode 100644 rust/hamming/.gitignore create mode 100644 rust/hamming/Cargo.toml create mode 100644 rust/hamming/HELP.md create mode 100644 rust/hamming/README.md create mode 100644 rust/hamming/src/lib.rs create mode 100644 rust/hamming/tests/hamming.rs create mode 100644 rust/isogram/.gitignore create mode 100644 rust/isogram/Cargo.toml create mode 100644 rust/isogram/HELP.md create mode 100644 rust/isogram/README.md create mode 100644 rust/isogram/src/lib.rs create mode 100644 rust/isogram/tests/isogram.rs create mode 100644 rust/leap/.gitignore create mode 100644 rust/leap/Cargo.toml create mode 100644 rust/leap/HELP.md create mode 100644 rust/leap/README.md create mode 100644 rust/leap/src/lib.rs create mode 100644 rust/leap/tests/leap.rs create mode 100644 rust/lucians-luscious-lasagna/.gitignore create mode 100644 rust/lucians-luscious-lasagna/Cargo.toml create mode 100644 rust/lucians-luscious-lasagna/HELP.md create mode 100644 rust/lucians-luscious-lasagna/HINTS.md create mode 100644 rust/lucians-luscious-lasagna/README.md create mode 100644 rust/lucians-luscious-lasagna/src/lib.rs create mode 100644 rust/lucians-luscious-lasagna/tests/lucians-luscious-lasagna.rs create mode 100644 rust/luhn/.gitignore create mode 100644 rust/luhn/Cargo.toml create mode 100644 rust/luhn/HELP.md create mode 100644 rust/luhn/README.md create mode 100644 rust/luhn/src/lib.rs create mode 100644 rust/luhn/tests/luhn.rs create mode 100644 rust/macros/.gitignore create mode 100644 rust/macros/Cargo.toml create mode 100644 rust/macros/HELP.md create mode 100644 rust/macros/README.md create mode 100644 rust/macros/src/lib.rs create mode 100644 rust/macros/tests/invalid/Cargo.toml create mode 100644 rust/macros/tests/invalid/comma-sep.rs create mode 100644 rust/macros/tests/invalid/double-commas.rs create mode 100644 rust/macros/tests/invalid/leading-comma.rs create mode 100644 rust/macros/tests/invalid/missing-argument.rs create mode 100644 rust/macros/tests/invalid/no-comma.rs create mode 100644 rust/macros/tests/invalid/only-arrow.rs create mode 100644 rust/macros/tests/invalid/only-comma.rs create mode 100644 rust/macros/tests/invalid/single-argument.rs create mode 100644 rust/macros/tests/invalid/triple-arguments.rs create mode 100644 rust/macros/tests/invalid/two-arrows.rs create mode 100644 rust/macros/tests/macros.rs create mode 100644 rust/matching-brackets/.gitignore create mode 100644 rust/matching-brackets/Cargo.toml create mode 100644 rust/matching-brackets/HELP.md create mode 100644 rust/matching-brackets/README.md create mode 100644 rust/matching-brackets/src/lib.rs create mode 100644 rust/matching-brackets/tests/matching-brackets.rs create mode 100644 rust/minesweeper/.gitignore create mode 100644 rust/minesweeper/Cargo.toml create mode 100644 rust/minesweeper/HELP.md create mode 100644 rust/minesweeper/README.md create mode 100644 rust/minesweeper/src/lib.rs create mode 100644 rust/minesweeper/tests/minesweeper.rs create mode 100644 rust/nucleotide-count/.gitignore create mode 100644 rust/nucleotide-count/Cargo.toml create mode 100644 rust/nucleotide-count/HELP.md create mode 100644 rust/nucleotide-count/README.md create mode 100644 rust/nucleotide-count/src/lib.rs create mode 100644 rust/nucleotide-count/tests/nucleotide-count.rs create mode 100644 rust/parallel-letter-frequency/.gitignore create mode 100644 rust/parallel-letter-frequency/Cargo.toml create mode 100644 rust/parallel-letter-frequency/HELP.md create mode 100644 rust/parallel-letter-frequency/README.md create mode 100644 rust/parallel-letter-frequency/benches/benchmark.rs create mode 100644 rust/parallel-letter-frequency/src/lib.rs create mode 100644 rust/parallel-letter-frequency/tests/parallel-letter-frequency.rs create mode 100644 rust/raindrops/.gitignore create mode 100644 rust/raindrops/Cargo.toml create mode 100644 rust/raindrops/HELP.md create mode 100644 rust/raindrops/README.md create mode 100644 rust/raindrops/src/lib.rs create mode 100644 rust/raindrops/tests/raindrops.rs create mode 100644 rust/reverse-string/.gitignore create mode 100644 rust/reverse-string/Cargo.toml create mode 100644 rust/reverse-string/HELP.md create mode 100644 rust/reverse-string/README.md create mode 100644 rust/reverse-string/src/lib.rs create mode 100644 rust/reverse-string/tests/reverse-string.rs create mode 100644 rust/robot-simulator/.gitignore create mode 100644 rust/robot-simulator/Cargo.toml create mode 100644 rust/robot-simulator/HELP.md create mode 100644 rust/robot-simulator/README.md create mode 100644 rust/robot-simulator/src/lib.rs create mode 100644 rust/robot-simulator/tests/robot-simulator.rs create mode 100644 rust/semi-structured-logs/.gitignore create mode 100644 rust/semi-structured-logs/Cargo.toml create mode 100644 rust/semi-structured-logs/HELP.md create mode 100644 rust/semi-structured-logs/HINTS.md create mode 100644 rust/semi-structured-logs/README.md create mode 100644 rust/semi-structured-logs/src/lib.rs create mode 100644 rust/semi-structured-logs/tests/semi-structured-logs.rs create mode 100644 rust/space-age/.gitignore create mode 100644 rust/space-age/Cargo.toml create mode 100644 rust/space-age/HELP.md create mode 100644 rust/space-age/README.md create mode 100644 rust/space-age/src/lib.rs create mode 100644 rust/space-age/tests/space-age.rs create mode 100644 rust/sublist/.gitignore create mode 100644 rust/sublist/Cargo.toml create mode 100644 rust/sublist/HELP.md create mode 100644 rust/sublist/README.md create mode 100644 rust/sublist/src/lib.rs create mode 100644 rust/sublist/src/main.rs create mode 100644 rust/sublist/tests/sublist.rs diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 00000000..daefe628 --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,2 @@ +.exercism/ +Cargo.lock \ No newline at end of file diff --git a/rust/all-your-base/.gitignore b/rust/all-your-base/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/all-your-base/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/all-your-base/Cargo.toml b/rust/all-your-base/Cargo.toml new file mode 100644 index 00000000..8ecd1109 --- /dev/null +++ b/rust/all-your-base/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "allyourbase" +version = "1.0.0" diff --git a/rust/all-your-base/HELP.md b/rust/all-your-base/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/all-your-base/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/all-your-base/README.md b/rust/all-your-base/README.md new file mode 100644 index 00000000..02a5675a --- /dev/null +++ b/rust/all-your-base/README.md @@ -0,0 +1,62 @@ +# All Your Base + +Welcome to All Your Base on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Convert a number, represented as a sequence of digits in one base, to any other base. + +Implement general base conversion. Given a number in base **a**, +represented as a sequence of digits, convert it to base **b**. + +## Note + +- Try to implement the conversion yourself. + Do not use something else to perform the conversion for you. + +## About [Positional Notation](https://en.wikipedia.org/wiki/Positional_notation) + +In positional notation, a number in base **b** can be understood as a linear +combination of powers of **b**. + +The number 42, *in base 10*, means: + +(4 * 10^1) + (2 * 10^0) + +The number 101010, *in base 2*, means: + +(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0) + +The number 1120, *in base 3*, means: + +(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0) + +I think you got the idea! + +*Yes. Those three numbers above are exactly the same. Congratulations!* + +## Source + +### Created by + +- @jonasbb + +### Contributed to by + +- @CGMossa +- @coriolinus +- @cwhakes +- @efx +- @ErikSchierboom +- @IanWhitney +- @lutostag +- @mkantor +- @navossoc +- @nfiles +- @pedantic79 +- @petertseng +- @rofrol +- @stringparser +- @xakon +- @ZapAnton \ No newline at end of file diff --git a/rust/all-your-base/src/lib.rs b/rust/all-your-base/src/lib.rs new file mode 100644 index 00000000..f7c4232f --- /dev/null +++ b/rust/all-your-base/src/lib.rs @@ -0,0 +1,65 @@ +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + InvalidInputBase, + InvalidOutputBase, + InvalidDigit(u32), +} + +/// +/// Convert a number between two bases. +/// +/// A number is any slice of digits. +/// A digit is any unsigned integer (e.g. u8, u16, u32, u64, or usize). +/// Bases are specified as unsigned integers. +/// +/// Return an `Err(.)` if the conversion is impossible. +/// The tests do not test for specific values inside the `Err(.)`. +/// +/// +/// You are allowed to change the function signature as long as all test still pass. +/// +/// +/// Example: +/// Input +/// number: &[4, 2] +/// from_base: 10 +/// to_base: 2 +/// Result +/// Ok(vec![1, 0, 1, 0, 1, 0]) +/// +/// The example corresponds to converting the number 42 from decimal +/// which is equivalent to 101010 in binary. +/// +/// +/// Notes: +/// * The empty slice ( "[]" ) is equal to the number 0. +/// * Never output leading 0 digits, unless the input number is 0, in which the output must be `[0]`. +/// However, your function must be able to process input with leading 0 digits. +/// +pub fn convert(number: &[u32], from_base: u32, to_base: u32) -> Result, Error> { + if from_base < 2 { + Err(Error::InvalidInputBase) + } else if to_base < 2 { + Err(Error::InvalidOutputBase) + } else { + // Intermediate decimal representation + let mut decimal = number.iter().try_fold(0, |acc, &d| { + if d >= from_base { + Err(Error::InvalidDigit(d)) + } else { + Ok(acc * from_base + d) + } + })?; + if decimal == 0 { + Ok(vec![0]) + } else { + let mut digits = vec![]; + while decimal > 0 { + digits.push(decimal % to_base); + decimal /= to_base; + } + digits.reverse(); + Ok(digits) + } + } +} \ No newline at end of file diff --git a/rust/all-your-base/tests/all-your-base.rs b/rust/all-your-base/tests/all-your-base.rs new file mode 100644 index 00000000..d4a5fc26 --- /dev/null +++ b/rust/all-your-base/tests/all-your-base.rs @@ -0,0 +1,200 @@ +use allyourbase as ayb; + +#[test] +fn single_bit_one_to_decimal() { + let input_base = 2; + let input_digits = &[1]; + let output_base = 10; + let output_digits = vec![1]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn binary_to_single_decimal() { + let input_base = 2; + let input_digits = &[1, 0, 1]; + let output_base = 10; + let output_digits = vec![5]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn single_decimal_to_binary() { + let input_base = 10; + let input_digits = &[5]; + let output_base = 2; + let output_digits = vec![1, 0, 1]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn binary_to_multiple_decimal() { + let input_base = 2; + let input_digits = &[1, 0, 1, 0, 1, 0]; + let output_base = 10; + let output_digits = vec![4, 2]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn decimal_to_binary() { + let input_base = 10; + let input_digits = &[4, 2]; + let output_base = 2; + let output_digits = vec![1, 0, 1, 0, 1, 0]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn trinary_to_hexadecimal() { + let input_base = 3; + let input_digits = &[1, 1, 2, 0]; + let output_base = 16; + let output_digits = vec![2, 10]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn hexadecimal_to_trinary() { + let input_base = 16; + let input_digits = &[2, 10]; + let output_base = 3; + let output_digits = vec![1, 1, 2, 0]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn fifteen_bit_integer() { + let input_base = 97; + let input_digits = &[3, 46, 60]; + let output_base = 73; + let output_digits = vec![6, 10, 45]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn empty_list() { + let input_base = 2; + let input_digits = &[]; + let output_base = 10; + let output_digits = vec![0]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn single_zero() { + let input_base = 10; + let input_digits = &[0]; + let output_base = 2; + let output_digits = vec![0]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn multiple_zeros() { + let input_base = 10; + let input_digits = &[0, 0, 0]; + let output_base = 2; + let output_digits = vec![0]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn leading_zeros() { + let input_base = 7; + let input_digits = &[0, 6, 0]; + let output_base = 10; + let output_digits = vec![4, 2]; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Ok(output_digits) + ); +} + +#[test] +fn invalid_positive_digit() { + let input_base = 2; + let input_digits = &[1, 2, 1, 0, 1, 0]; + let output_base = 10; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Err(ayb::Error::InvalidDigit(2)) + ); +} + +#[test] +fn input_base_is_one() { + let input_base = 1; + let input_digits = &[]; + let output_base = 10; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Err(ayb::Error::InvalidInputBase) + ); +} + +#[test] +fn output_base_is_one() { + let input_base = 2; + let input_digits = &[1, 0, 1, 0, 1, 0]; + let output_base = 1; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Err(ayb::Error::InvalidOutputBase) + ); +} + +#[test] +fn input_base_is_zero() { + let input_base = 0; + let input_digits = &[]; + let output_base = 10; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Err(ayb::Error::InvalidInputBase) + ); +} + +#[test] +fn output_base_is_zero() { + let input_base = 10; + let input_digits = &[7]; + let output_base = 0; + assert_eq!( + ayb::convert(input_digits, input_base, output_base), + Err(ayb::Error::InvalidOutputBase) + ); +} diff --git a/rust/armstrong-numbers/.gitignore b/rust/armstrong-numbers/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/armstrong-numbers/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/armstrong-numbers/Cargo.toml b/rust/armstrong-numbers/Cargo.toml new file mode 100644 index 00000000..4949ea7f --- /dev/null +++ b/rust/armstrong-numbers/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "armstrong_numbers" +version = "1.1.0" diff --git a/rust/armstrong-numbers/HELP.md b/rust/armstrong-numbers/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/armstrong-numbers/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/armstrong-numbers/README.md b/rust/armstrong-numbers/README.md new file mode 100644 index 00000000..bbeea2bd --- /dev/null +++ b/rust/armstrong-numbers/README.md @@ -0,0 +1,44 @@ +# Armstrong Numbers + +Welcome to Armstrong Numbers on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +An [Armstrong number](https://en.wikipedia.org/wiki/Narcissistic_number) is a number that is the sum of its own digits each raised to the power of the number of digits. + +For example: + +- 9 is an Armstrong number, because `9 = 9^1 = 9` +- 10 is *not* an Armstrong number, because `10 != 1^2 + 0^2 = 1` +- 153 is an Armstrong number, because: `153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153` +- 154 is *not* an Armstrong number, because: `154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190` + +Write some code to determine whether a number is an Armstrong number. + +## Source + +### Created by + +- @shingtaklam1324 + +### Contributed to by + +- @AndrewKvalheim +- @coriolinus +- @cwhakes +- @eddyp +- @efx +- @ErikSchierboom +- @lutostag +- @ocstl +- @petertseng +- @rofrol +- @sputnick1124 +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +Wikipedia - https://en.wikipedia.org/wiki/Narcissistic_number \ No newline at end of file diff --git a/rust/armstrong-numbers/src/lib.rs b/rust/armstrong-numbers/src/lib.rs new file mode 100644 index 00000000..bb701554 --- /dev/null +++ b/rust/armstrong-numbers/src/lib.rs @@ -0,0 +1,21 @@ +pub fn is_armstrong_number(num: u32) -> bool { + to_digits(num.into()) + .iter() + .fold(0, |acc, el| acc + el.pow(num.to_string().len() as u32)) + == num.into() +} + +fn to_digits(num: u64) -> Vec { + if num > 0 { + let mut digits = vec![]; + let mut n = num; + while n > 0 { + digits.push(n % 10); + n = n / 10; + } + digits.reverse(); + digits + } else { + return vec![0]; + } +} diff --git a/rust/armstrong-numbers/tests/armstrong-numbers.rs b/rust/armstrong-numbers/tests/armstrong-numbers.rs new file mode 100644 index 00000000..d621c65d --- /dev/null +++ b/rust/armstrong-numbers/tests/armstrong-numbers.rs @@ -0,0 +1,69 @@ +use armstrong_numbers::*; + +#[test] +fn test_zero_is_an_armstrong_number() { + assert!(is_armstrong_number(0)) +} + +#[test] +fn test_single_digit_numbers_are_armstrong_numbers() { + assert!(is_armstrong_number(5)) +} + +#[test] +fn test_there_are_no_2_digit_armstrong_numbers() { + assert!(!is_armstrong_number(10)) +} + +#[test] +fn test_three_digit_armstrong_number() { + assert!(is_armstrong_number(153)) +} + +#[test] +fn test_three_digit_non_armstrong_number() { + assert!(!is_armstrong_number(100)) +} + +#[test] +fn test_four_digit_armstrong_number() { + assert!(is_armstrong_number(9474)) +} + +#[test] +fn test_four_digit_non_armstrong_number() { + assert!(!is_armstrong_number(9475)) +} + +#[test] +fn test_seven_digit_armstrong_number() { + assert!(is_armstrong_number(9_926_315)) +} + +#[test] +fn test_seven_digit_non_armstrong_number() { + assert!(!is_armstrong_number(9_926_316)) +} + +#[test] +fn test_nine_digit_armstrong_number() { + assert!(is_armstrong_number(912_985_153)); +} + +#[test] +fn test_nine_digit_non_armstrong_number() { + assert!(!is_armstrong_number(999_999_999)); +} + +#[test] +fn test_ten_digit_non_armstrong_number() { + assert!(!is_armstrong_number(3_999_999_999)); +} + +// The following number has an Armstrong sum equal to 2^32 plus itself, +// and therefore will be detected as an Armstrong number if you are +// incorrectly using wrapping arithmetic. +#[test] +fn test_properly_handles_overflow() { + assert!(!is_armstrong_number(4_106_098_957)); +} diff --git a/rust/assembly-line/.gitignore b/rust/assembly-line/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/assembly-line/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/assembly-line/Cargo.toml b/rust/assembly-line/Cargo.toml new file mode 100644 index 00000000..5a7f2c4c --- /dev/null +++ b/rust/assembly-line/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "assembly-line" +version = "0.1.0" +edition = "2021" diff --git a/rust/assembly-line/HELP.md b/rust/assembly-line/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/assembly-line/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/assembly-line/HINTS.md b/rust/assembly-line/HINTS.md new file mode 100644 index 00000000..43fcd81e --- /dev/null +++ b/rust/assembly-line/HINTS.md @@ -0,0 +1,12 @@ +# Hints + +## General + +## 1. Calculate the production rate per hour + +- Determining the success rate can be done through a [conditional statement](https://doc.rust-lang.org/stable/book/ch03-05-control-flow.html#if-expressions) or with [pattern matching](https://doc.rust-lang.org/stable/book/ch18-01-all-the-places-for-patterns.html#match-arms). +- As Rust only allows multiplication between values of the same type, some [type casting](https://doc.rust-lang.org/rust-by-example/types/cast.html) will have to be done. + +## 2. Calculate the number of working items produced per minute + +- Just like multiplication, division is only possible between numbers of the same type. By writing a number with a decimal point (e.g. `1.0` instead of `1`) we can write inline constants with a floating point type. \ No newline at end of file diff --git a/rust/assembly-line/README.md b/rust/assembly-line/README.md new file mode 100644 index 00000000..15164a4a --- /dev/null +++ b/rust/assembly-line/README.md @@ -0,0 +1,88 @@ +# Assembly Line + +Welcome to Assembly Line on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +## Numbers + +There are two different categories of numbers in Rust: integers (which can be signed or unsigned) and floating-point numbers. + +## Integers + +- Integers: numbers with no digits behind the decimal separator (whole numbers). + Integer types can either store only positive numbers (unsigned) or store either positive and negative numbers (signed). + Examples are -6, 0, 1, 25, 976 and 500000. + +## Floating-Point Numbers + +- Floating-point numbers: numbers with zero or more digits behind the decimal separator. + Examples are -2.4, 0.1, 3.14, 16.984025 and 1024.0. + +## Naming numeric types + +The name of a numeric type consists of two parts: + +- A letter to specify whether it's an unsigned integer (u), signed integer (i), or floating-point number (f). +- A number to specify the type's size in bits. Larger types have a greater range between minimum and maximum values. + For floating points it will also allow for more numbers behind the decimal separator. + +The following combinations are possible: + +- 8 bits: `u8`, `i8` +- 16 bits: `u16`, `i16` +- 32 bits: `u32`, `i32`, `f32` +- 64 bits: `u64`, `i64`, `f64` +- 128 bits: `u128`, `i128` + +Note that there are only 32-bits and 64-bits variants for floating-point numbers. + +## Converting between number types + +Rust doesn't do any implicit type conversion. +This means that if you need to turn one numeric type into another, you have to do so explicitly. +When converting from a larger type to a smaller one (for instance `u64` to `u32`) you could lose data. +Converting from a floating point to an integer **will** lose everything behind the decimal point, effectively rounding down. + +## Instructions + +In this exercise you'll be writing code to analyze the production of an assembly line in a car factory. The assembly line's speed can range from `0` (off) to `10` (maximum). + +At its lowest speed (`1`), `221` cars are produced each hour. The production increases linearly with the speed. So with the speed set to `4`, it should produce `4 * 221 = 884` cars per hour. However, higher speeds increase the likelihood that faulty cars are produced, which then have to be discarded. The following table shows how speed influences the success rate: + +- `1` to `4`: 100% success rate. +- `5` to `8`: 90% success rate. +- `9` and `10`: 77% success rate. + +You have two tasks. + +## 1. Calculate the production rate per hour + +Implement a method to calculate the assembly line's production rate per hour, taking into account its success rate: + +```rust +assembly_line::production_rate_per_hour(6) +// Returns: 1193.4 +``` + +Note that the value returned is an `f64`. + +## 2. Calculate the number of working items produced per minute + +Implement a method to calculate how many working cars are produced per minute: + +```rust +assembly_line::working_items_per_minute(6) +// Returns: 19 +``` + +Note that the value returned is an `u32`. + +## Source + +### Created by + +- @LewisClement +- @efx \ No newline at end of file diff --git a/rust/assembly-line/src/lib.rs b/rust/assembly-line/src/lib.rs new file mode 100644 index 00000000..7f64c5d3 --- /dev/null +++ b/rust/assembly-line/src/lib.rs @@ -0,0 +1,19 @@ +#![allow(unused)] + +const HOURLY_RATE: u32 = 221; + +fn success_rate(speed: u8) -> f64 { + match speed { + 0..=4 => 1.0, + 5..=8 => 0.9, + _ => 0.77, + } +} + +pub fn production_rate_per_hour(speed: u8) -> f64 { + return ((speed as u32 * HOURLY_RATE) as f64) * success_rate(speed); +} + +pub fn working_items_per_minute(speed: u8) -> u32 { + return (production_rate_per_hour(speed) / 60.0) as u32; +} diff --git a/rust/assembly-line/tests/assembly-line.rs b/rust/assembly-line/tests/assembly-line.rs new file mode 100644 index 00000000..3696c128 --- /dev/null +++ b/rust/assembly-line/tests/assembly-line.rs @@ -0,0 +1,65 @@ +use assembly_line::production_rate_per_hour; + +fn process_rate_per_hour(speed: u8, expected_rate: f64) { + let actual_rate = assembly_line::production_rate_per_hour(speed); + let actual_rate = (actual_rate * 100.0).round() / 100.0; + assert!((actual_rate - expected_rate).abs() < f64::EPSILON); +} + +fn process_rate_per_minute(speed: u8, expected_rate: u32) { + assert_eq!( + assembly_line::working_items_per_minute(speed), + expected_rate + ); +} + +#[test] +fn production_rate_per_hour_at_speed_zero() { + process_rate_per_hour(0, 0.0); +} + +#[test] +fn production_rate_per_hour_at_speed_one() { + process_rate_per_hour(1, 221.0); +} + +#[test] +fn production_rate_per_hour_at_speed_four() { + process_rate_per_hour(4, 884.0); +} + +#[test] +fn production_rate_per_hour_at_speed_seven() { + print!("DEBUG {}", production_rate_per_hour(7)); + process_rate_per_hour(7, 1392.3); +} + +#[test] +fn production_rate_per_hour_at_speed_nine() { + process_rate_per_hour(9, 1531.53); +} + +#[test] +fn production_rate_per_minute_at_speed_zero() { + process_rate_per_minute(0, 0); +} + +#[test] +fn production_rate_per_minute_at_speed_one() { + process_rate_per_minute(1, 3); +} + +#[test] +fn production_rate_per_minute_at_speed_five() { + process_rate_per_minute(5, 16); +} + +#[test] +fn production_rate_per_minute_at_speed_eight() { + process_rate_per_minute(8, 26); +} + +#[test] +fn production_rate_per_minute_at_speed_ten() { + process_rate_per_minute(10, 28); +} diff --git a/rust/difference-of-squares/.gitignore b/rust/difference-of-squares/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/difference-of-squares/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/difference-of-squares/Cargo.toml b/rust/difference-of-squares/Cargo.toml new file mode 100644 index 00000000..71d56f82 --- /dev/null +++ b/rust/difference-of-squares/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "difference-of-squares" +version = "1.2.0" diff --git a/rust/difference-of-squares/HELP.md b/rust/difference-of-squares/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/difference-of-squares/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/difference-of-squares/README.md b/rust/difference-of-squares/README.md new file mode 100644 index 00000000..01319d5e --- /dev/null +++ b/rust/difference-of-squares/README.md @@ -0,0 +1,54 @@ +# Difference of Squares + +Welcome to Difference of Squares on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Find the difference between the square of the sum and the sum of the squares of the first N natural numbers. + +The square of the sum of the first ten natural numbers is +(1 + 2 + ... + 10)² = 55² = 3025. + +The sum of the squares of the first ten natural numbers is +1² + 2² + ... + 10² = 385. + +Hence the difference between the square of the sum of the first +ten natural numbers and the sum of the squares of the first ten +natural numbers is 3025 - 385 = 2640. + +You are not expected to discover an efficient solution to this yourself from +first principles; research is allowed, indeed, encouraged. Finding the best +algorithm for the problem is a key skill in software engineering. + +## Source + +### Created by + +- @EduardoBautista + +### Contributed to by + +- @ashleygwilliams +- @coriolinus +- @cwhakes +- @eddyp +- @EduardoBautista +- @efx +- @ErikSchierboom +- @IanWhitney +- @kytrinyx +- @leoyvens +- @lutostag +- @mkantor +- @nfiles +- @petertseng +- @rofrol +- @stringparser +- @TheDarkula +- @xakon +- @ZapAnton + +### Based on + +Problem 6 at Project Euler - http://projecteuler.net/problem=6 \ No newline at end of file diff --git a/rust/difference-of-squares/src/lib.rs b/rust/difference-of-squares/src/lib.rs new file mode 100644 index 00000000..c76300b5 --- /dev/null +++ b/rust/difference-of-squares/src/lib.rs @@ -0,0 +1,11 @@ +pub fn square_of_sum(n: u32) -> u32 { + (|x: u32| x * x)((1..=n).sum()) +} + +pub fn sum_of_squares(n: u32) -> u32 { + (1..=n).map(|x| x * x).sum() +} + +pub fn difference(n: u32) -> u32 { + square_of_sum(n) - sum_of_squares(n) +} diff --git a/rust/difference-of-squares/tests/difference-of-squares.rs b/rust/difference-of-squares/tests/difference-of-squares.rs new file mode 100644 index 00000000..4a029b3b --- /dev/null +++ b/rust/difference-of-squares/tests/difference-of-squares.rs @@ -0,0 +1,46 @@ +use difference_of_squares as squares; + +#[test] +fn test_square_of_sum_1() { + assert_eq!(1, squares::square_of_sum(1)); +} + +#[test] +fn test_square_of_sum_5() { + assert_eq!(225, squares::square_of_sum(5)); +} + +#[test] +fn test_square_of_sum_100() { + assert_eq!(25_502_500, squares::square_of_sum(100)); +} + +#[test] +fn test_sum_of_squares_1() { + assert_eq!(1, squares::sum_of_squares(1)); +} + +#[test] +fn test_sum_of_squares_5() { + assert_eq!(55, squares::sum_of_squares(5)); +} + +#[test] +fn test_sum_of_squares_100() { + assert_eq!(338_350, squares::sum_of_squares(100)); +} + +#[test] +fn test_difference_1() { + assert_eq!(0, squares::difference(1)); +} + +#[test] +fn test_difference_5() { + assert_eq!(170, squares::difference(5)); +} + +#[test] +fn test_difference_100() { + assert_eq!(25_164_150, squares::difference(100)); +} diff --git a/rust/etl/.gitignore b/rust/etl/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/etl/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/etl/Cargo.toml b/rust/etl/Cargo.toml new file mode 100644 index 00000000..67624abe --- /dev/null +++ b/rust/etl/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "etl" +version = "1.0.0" diff --git a/rust/etl/HELP.md b/rust/etl/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/etl/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/etl/README.md b/rust/etl/README.md new file mode 100644 index 00000000..87d72e49 --- /dev/null +++ b/rust/etl/README.md @@ -0,0 +1,82 @@ +# ETL + +Welcome to ETL on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Introduction + +You work for a company that makes an online multiplayer game called Lexiconia. + +To play the game, each player is given 13 letters, which they must rearrange to create words. +Different letters have different point values, since it's easier to create words with some letters than others. + +The game was originally launched in English, but it is very popular, and now the company wants to expand to other languages as well. + +Different languages need to support different point values for letters. +The point values are determined by how often letters are used, compared to other letters in that language. + +For example, the letter 'C' is quite common in English, and is only worth 3 points. +But in Norwegian it's a very rare letter, and is worth 10 points. + +To make it easier to add new languages, your team needs to change the way letters and their point values are stored in the game. + +## Instructions + +Your task is to change the data format of letters and their point values in the game. + +Currently, letters are stored in groups based on their score, in a one-to-many mapping. + +- 1 point: "A", "E", "I", "O", "U", "L", "N", "R", "S", "T", +- 2 points: "D", "G", +- 3 points: "B", "C", "M", "P", +- 4 points: "F", "H", "V", "W", "Y", +- 5 points: "K", +- 8 points: "J", "X", +- 10 points: "Q", "Z", + +This needs to be changed to store each individual letter with its score in a one-to-one mapping. + +- "a" is worth 1 point. +- "b" is worth 3 points. +- "c" is worth 3 points. +- "d" is worth 2 points. +- etc. + +As part of this change, the team has also decided to change the letters to be lower-case rather than upper-case. + +~~~~exercism/note +If you want to look at how the data was previously structured and how it needs to change, take a look at the examples in the test suite. +~~~~ + +## Source + +### Created by + +- @EduardoBautista + +### Contributed to by + +- @ashleygwilliams +- @ClashTheBunny +- @coriolinus +- @cwhakes +- @EduardoBautista +- @efx +- @ErikSchierboom +- @IanWhitney +- @kotp +- @kytrinyx +- @lutostag +- @mkantor +- @navossoc +- @nfiles +- @petertseng +- @rofrol +- @stringparser +- @tushartyagi +- @xakon +- @ZapAnton + +### Based on + +The Jumpstart Lab team - http://jumpstartlab.com \ No newline at end of file diff --git a/rust/etl/src/lib.rs b/rust/etl/src/lib.rs new file mode 100644 index 00000000..07677c69 --- /dev/null +++ b/rust/etl/src/lib.rs @@ -0,0 +1,11 @@ +use std::collections::BTreeMap; + +pub fn transform(h: &BTreeMap>) -> BTreeMap { + let mut map = BTreeMap::new(); + h.iter().for_each(|(points, chars)| { + chars.iter().for_each(|c| { + map.insert(c.to_ascii_lowercase(), *points); + }) + }); + map +} diff --git a/rust/etl/tests/etl.rs b/rust/etl/tests/etl.rs new file mode 100644 index 00000000..e8e1e011 --- /dev/null +++ b/rust/etl/tests/etl.rs @@ -0,0 +1,80 @@ +use std::collections::BTreeMap; + +#[test] +fn test_transform_one_value() { + let input = input_from(&[(1, vec!['A'])]); + + let expected = expected_from(&[('a', 1)]); + + assert_eq!(expected, etl::transform(&input)); +} + +#[test] +fn test_transform_more_values() { + let input = input_from(&[(1, vec!['A', 'E', 'I', 'O', 'U'])]); + + let expected = expected_from(&[('a', 1), ('e', 1), ('i', 1), ('o', 1), ('u', 1)]); + + assert_eq!(expected, etl::transform(&input)); +} + +#[test] +fn test_more_keys() { + let input = input_from(&[(1, vec!['A', 'E']), (2, vec!['D', 'G'])]); + + let expected = expected_from(&[('a', 1), ('e', 1), ('d', 2), ('g', 2)]); + + assert_eq!(expected, etl::transform(&input)); +} + +#[test] +fn test_full_dataset() { + let input = input_from(&[ + (1, vec!['A', 'E', 'I', 'O', 'U', 'L', 'N', 'R', 'S', 'T']), + (2, vec!['D', 'G']), + (3, vec!['B', 'C', 'M', 'P']), + (4, vec!['F', 'H', 'V', 'W', 'Y']), + (5, vec!['K']), + (8, vec!['J', 'X']), + (10, vec!['Q', 'Z']), + ]); + + let expected = expected_from(&[ + ('a', 1), + ('b', 3), + ('c', 3), + ('d', 2), + ('e', 1), + ('f', 4), + ('g', 2), + ('h', 4), + ('i', 1), + ('j', 8), + ('k', 5), + ('l', 1), + ('m', 3), + ('n', 1), + ('o', 1), + ('p', 3), + ('q', 10), + ('r', 1), + ('s', 1), + ('t', 1), + ('u', 1), + ('v', 4), + ('w', 4), + ('x', 8), + ('y', 4), + ('z', 10), + ]); + + assert_eq!(expected, etl::transform(&input)); +} + +fn input_from(v: &[(i32, Vec)]) -> BTreeMap> { + v.iter().cloned().collect() +} + +fn expected_from(v: &[(char, i32)]) -> BTreeMap { + v.iter().cloned().collect() +} diff --git a/rust/gigasecond/.gitignore b/rust/gigasecond/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/gigasecond/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/gigasecond/Cargo.toml b/rust/gigasecond/Cargo.toml new file mode 100644 index 00000000..da110b7b --- /dev/null +++ b/rust/gigasecond/Cargo.toml @@ -0,0 +1,7 @@ +[package] +edition = "2021" +name = "gigasecond" +version = "2.0.0" + +[dependencies] +time = "0.3" diff --git a/rust/gigasecond/HELP.md b/rust/gigasecond/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/gigasecond/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/gigasecond/README.md b/rust/gigasecond/README.md new file mode 100644 index 00000000..9042a24d --- /dev/null +++ b/rust/gigasecond/README.md @@ -0,0 +1,76 @@ +# Gigasecond + +Welcome to Gigasecond on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Introduction + +The way we measure time is kind of messy. +We have 60 seconds in a minute, and 60 minutes in an hour. +This comes from ancient Babylon, where they used 60 as the basis for their number system. +We have 24 hours in a day, 7 days in a week, and how many days in a month? +Well, for days in a month it depends not only on which month it is, but also on what type of calendar is used in the country you live in. + +What if, instead, we only use seconds to express time intervals? +Then we can use metric system prefixes for writing large numbers of seconds in more easily comprehensible quantities. + +- A food recipe might explain that you need to let the brownies cook in the oven for two kiloseconds (that's two thousand seconds). +- Perhaps you and your family would travel to somewhere exotic for two megaseconds (that's two million seconds). +- And if you and your spouse were married for _a thousand million_ seconds, you would celebrate your one gigasecond anniversary. + +```exercism/note +If we ever colonize Mars or some other planet, measuring time is going to get even messier. +If someone says "year" do they mean a year on Earth or a year on Mars? + +The idea for this exercise came from the science fiction novel ["A Deepness in the Sky"][vinge-novel] by author Vernor Vinge. +In it the author uses the metric system as the basis for time measurements. + +[vinge-novel]: https://www.tor.com/2017/08/03/science-fiction-with-something-for-everyone-a-deepness-in-the-sky-by-vernor-vinge/ +``` + +## Instructions + +Your task is to determine the date and time one gigasecond after a certain date. + +A gigasecond is one thousand million seconds. +That is a one with nine zeros after it. + +If you were born on _January 24th, 2015 at 22:00 (10:00:00pm)_, then you would be a gigasecond old on _October 2nd, 2046 at 23:46:40 (11:46:40pm)_. + +If you're unsure what operations you can perform on `PrimitiveDateTime` take a look at the [time crate](https://docs.rs/time) which is listed as a dependency in the `Cargo.toml` file for this exercise. + +## Source + +### Created by + +- @IanWhitney + +### Contributed to by + +- @andy5995 +- @ashleygwilliams +- @cbzehner +- @coriolinus +- @cwhakes +- @EduardoBautista +- @efx +- @ErikSchierboom +- @houhoulis +- @IanWhitney +- @janczer +- @leoyvens +- @lutostag +- @mkantor +- @nfiles +- @NieDzejkob +- @ocstl +- @petertseng +- @rofrol +- @sacherjj +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +Chapter 9 in Chris Pine's online Learn to Program tutorial. - http://pine.fm/LearnToProgram/?Chapter=09 \ No newline at end of file diff --git a/rust/gigasecond/src/lib.rs b/rust/gigasecond/src/lib.rs new file mode 100644 index 00000000..af632379 --- /dev/null +++ b/rust/gigasecond/src/lib.rs @@ -0,0 +1,8 @@ +use time::{Duration, PrimitiveDateTime as DateTime}; + +// Returns a DateTime one billion seconds after start. +pub fn after(start: DateTime) -> DateTime { + start + .checked_add(Duration::seconds(1_000_000_000)) + .expect("Cannot perform DateTime arithmetic") +} diff --git a/rust/gigasecond/tests/gigasecond.rs b/rust/gigasecond/tests/gigasecond.rs new file mode 100644 index 00000000..a3e7dc05 --- /dev/null +++ b/rust/gigasecond/tests/gigasecond.rs @@ -0,0 +1,48 @@ +use time::PrimitiveDateTime as DateTime; + +/// Create a datetime from the given numeric point in time. +/// +/// Panics if any field is invalid. +fn dt(year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> DateTime { + use time::{Date, Time}; + + DateTime::new( + Date::from_calendar_date(year, month.try_into().unwrap(), day).unwrap(), + Time::from_hms(hour, minute, second).unwrap(), + ) +} + +#[test] +fn test_date() { + let start_date = dt(2011, 4, 25, 0, 0, 0); + + assert_eq!(gigasecond::after(start_date), dt(2043, 1, 1, 1, 46, 40)); +} + +#[test] +fn test_another_date() { + let start_date = dt(1977, 6, 13, 0, 0, 0); + + assert_eq!(gigasecond::after(start_date), dt(2009, 2, 19, 1, 46, 40)); +} + +#[test] +fn test_third_date() { + let start_date = dt(1959, 7, 19, 0, 0, 0); + + assert_eq!(gigasecond::after(start_date), dt(1991, 3, 27, 1, 46, 40)); +} + +#[test] +fn test_datetime() { + let start_date = dt(2015, 1, 24, 22, 0, 0); + + assert_eq!(gigasecond::after(start_date), dt(2046, 10, 2, 23, 46, 40)); +} + +#[test] +fn test_another_datetime() { + let start_date = dt(2015, 1, 24, 23, 59, 59); + + assert_eq!(gigasecond::after(start_date), dt(2046, 10, 3, 1, 46, 39)); +} diff --git a/rust/grains/.gitignore b/rust/grains/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/grains/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/grains/Cargo.toml b/rust/grains/Cargo.toml new file mode 100644 index 00000000..077822d6 --- /dev/null +++ b/rust/grains/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "grains" +version = "1.2.0" diff --git a/rust/grains/HELP.md b/rust/grains/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/grains/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/grains/README.md b/rust/grains/README.md new file mode 100644 index 00000000..58259c12 --- /dev/null +++ b/rust/grains/README.md @@ -0,0 +1,61 @@ +# Grains + +Welcome to Grains on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Calculate the number of grains of wheat on a chessboard given that the number +on each square doubles. + +There once was a wise servant who saved the life of a prince. The king +promised to pay whatever the servant could dream up. Knowing that the +king loved chess, the servant told the king he would like to have grains +of wheat. One grain on the first square of a chess board, with the number +of grains doubling on each successive square. + +There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on). + +Write code that shows: +- how many grains were on a given square, +- the total number of grains on the chessboard, and +- panics with a message of "Square must be between 1 and 64" if the value is not valid + +## For bonus points + +Did you get the tests passing and the code clean? If you want to, these +are some additional things you could try: + +- Optimize for speed. +- Optimize for readability. + +Then please share your thoughts in a comment on the submission. Did this +experiment make the code better? Worse? Did you learn anything from it? + +## Source + +### Created by + +- @IanWhitney + +### Contributed to by + +- @ClashTheBunny +- @coriolinus +- @cwhakes +- @efx +- @ErikSchierboom +- @krodyrobi +- @lutostag +- @navossoc +- @nfiles +- @petertseng +- @rofrol +- @stringparser +- @TheDarkula +- @xakon +- @ZapAnton + +### Based on + +JavaRanch Cattle Drive, exercise 6 - http://www.javaranch.com/grains.jsp \ No newline at end of file diff --git a/rust/grains/src/lib.rs b/rust/grains/src/lib.rs new file mode 100644 index 00000000..3113fbe0 --- /dev/null +++ b/rust/grains/src/lib.rs @@ -0,0 +1,10 @@ +pub fn square(s: u32) -> u64 { + if s < 1 || s > 64 { + panic!("Square must be between 1 and 64"); + } + 1u64 << (s - 1) +} + +pub fn total() -> u64 { + u64::MAX +} diff --git a/rust/grains/tests/grains.rs b/rust/grains/tests/grains.rs new file mode 100644 index 00000000..a8374884 --- /dev/null +++ b/rust/grains/tests/grains.rs @@ -0,0 +1,63 @@ +fn process_square_case(input: u32, expected: u64) { + assert_eq!(grains::square(input), expected); +} + +#[test] +/// 1 +fn test_1() { + process_square_case(1, 1); +} + +#[test] +/// 2 +fn test_2() { + process_square_case(2, 2); +} + +#[test] +/// 3 +fn test_3() { + process_square_case(3, 4); +} + +#[test] +/// 4 +fn test_4() { + process_square_case(4, 8); +} + +//NEW +#[test] +/// 16 +fn test_16() { + process_square_case(16, 32_768); +} + +#[test] +/// 32 +fn test_32() { + process_square_case(32, 2_147_483_648); +} + +#[test] +/// 64 +fn test_64() { + process_square_case(64, 9_223_372_036_854_775_808); +} + +#[test] +#[should_panic(expected = "Square must be between 1 and 64")] +fn test_square_0_raises_an_exception() { + grains::square(0); +} + +#[test] +#[should_panic(expected = "Square must be between 1 and 64")] +fn test_square_greater_than_64_raises_an_exception() { + grains::square(65); +} + +#[test] +fn test_returns_the_total_number_of_grains_on_the_board() { + assert_eq!(grains::total(), 18_446_744_073_709_551_615); +} diff --git a/rust/hamming/.gitignore b/rust/hamming/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/hamming/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/hamming/Cargo.toml b/rust/hamming/Cargo.toml new file mode 100644 index 00000000..0217a7a9 --- /dev/null +++ b/rust/hamming/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "hamming" +version = "2.2.0" diff --git a/rust/hamming/HELP.md b/rust/hamming/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/hamming/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/hamming/README.md b/rust/hamming/README.md new file mode 100644 index 00000000..233cc72b --- /dev/null +++ b/rust/hamming/README.md @@ -0,0 +1,59 @@ +# Hamming + +Welcome to Hamming on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Calculate the Hamming Distance between two DNA strands. + +Your body is made up of cells that contain DNA. Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime! + +When cells divide, their DNA replicates too. Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. If we compare two strands of DNA and count the differences between them we can see how many mistakes occurred. This is known as the "Hamming Distance". + +We read DNA using the letters C,A,G and T. Two strands might look like this: + + GAGCCTACTAACGGGAT + CATCGTAATGACGGCCT + ^ ^ ^ ^ ^ ^^ + +They have 7 differences, and therefore the Hamming Distance is 7. + +The Hamming Distance is useful for lots of things in science, not just biology, so it's a nice phrase to be familiar with :) + +The Hamming distance is only defined for sequences of equal length, so +an attempt to calculate it between sequences of different lengths should +not work. The general handling of this situation (e.g., raising an +exception vs returning a special value) may differ between languages. + +## Source + +### Created by + +- @IanWhitney + +### Contributed to by + +- @ashleygwilliams +- @AvasDream +- @ClashTheBunny +- @coriolinus +- @cwhakes +- @efx +- @ErikSchierboom +- @Henning-K +- @IanWhitney +- @jonasbb +- @kytrinyx +- @lutostag +- @mkantor +- @nfiles +- @petertseng +- @rofrol +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +The Calculating Point Mutations problem at Rosalind - http://rosalind.info/problems/hamm/ \ No newline at end of file diff --git a/rust/hamming/src/lib.rs b/rust/hamming/src/lib.rs new file mode 100644 index 00000000..0fa5528e --- /dev/null +++ b/rust/hamming/src/lib.rs @@ -0,0 +1,7 @@ +pub fn hamming_distance(s1: &str, s2: &str) -> Option { + (s1.len() == s2.len()).then(|| { + s1.chars() + .zip(s2.chars()) + .fold(0, |acc, (c1, c2)| acc + if c1 == c2 { 0 } else { 1 }) + }) +} diff --git a/rust/hamming/tests/hamming.rs b/rust/hamming/tests/hamming.rs new file mode 100644 index 00000000..8dd23235 --- /dev/null +++ b/rust/hamming/tests/hamming.rs @@ -0,0 +1,77 @@ +fn process_distance_case(strand_pair: [&str; 2], expected_distance: Option) { + assert_eq!( + hamming::hamming_distance(strand_pair[0], strand_pair[1]), + expected_distance + ); +} + +#[test] +fn test_empty_strands() { + process_distance_case(["", ""], Some(0)); +} + +#[test] +/// disallow first strand longer +fn test_disallow_first_strand_longer() { + process_distance_case(["AATG", "AAA"], None); +} + +#[test] +/// disallow second strand longer +fn test_disallow_second_strand_longer() { + process_distance_case(["ATA", "AGTG"], None); +} + +#[test] +fn test_first_string_is_longer() { + process_distance_case(["AAA", "AA"], None); +} + +#[test] +fn test_second_string_is_longer() { + process_distance_case(["A", "AA"], None); +} + +#[test] +/// single letter identical strands +fn test_single_letter_identical_strands() { + process_distance_case(["A", "A"], Some(0)); +} + +#[test] +/// small distance +fn test_single_letter_different_strands() { + process_distance_case(["G", "T"], Some(1)); +} + +#[test] +/// long identical strands +fn test_long_identical_strands() { + process_distance_case(["GGACTGAAATCTG", "GGACTGAAATCTG"], Some(0)); +} + +#[test] +fn test_no_difference_between_identical_strands() { + process_distance_case(["GGACTGA", "GGACTGA"], Some(0)); +} + +#[test] +fn test_complete_hamming_distance_in_small_strand() { + process_distance_case(["ACT", "GGA"], Some(3)); +} + +#[test] +fn test_small_hamming_distance_in_the_middle_somewhere() { + process_distance_case(["GGACG", "GGTCG"], Some(1)); +} + +#[test] +fn test_larger_distance() { + process_distance_case(["ACCAGGG", "ACTATGG"], Some(2)); +} + +#[test] +/// large distance in off-by-one strand +fn test_long_different_strands() { + process_distance_case(["GGACGGATTCTG", "AGGACGGATTCT"], Some(9)); +} diff --git a/rust/isogram/.gitignore b/rust/isogram/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/isogram/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/isogram/Cargo.toml b/rust/isogram/Cargo.toml new file mode 100644 index 00000000..671cd99d --- /dev/null +++ b/rust/isogram/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "isogram" +version = "1.3.0" diff --git a/rust/isogram/HELP.md b/rust/isogram/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/isogram/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/isogram/README.md b/rust/isogram/README.md new file mode 100644 index 00000000..efd7346d --- /dev/null +++ b/rust/isogram/README.md @@ -0,0 +1,42 @@ +# Isogram + +Welcome to Isogram on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Determine if a word or phrase is an isogram. + +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. + +Examples of isograms: + +- lumberjacks +- background +- downstream +- six-year-old + +The word *isograms*, however, is not an isogram, because the s repeats. + +## Source + +### Created by + +- @hekrause + +### Contributed to by + +- @AvasDream +- @coriolinus +- @cwhakes +- @efx +- @ErikSchierboom +- @petertseng +- @rofrol +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +Wikipedia - https://en.wikipedia.org/wiki/Isogram \ No newline at end of file diff --git a/rust/isogram/src/lib.rs b/rust/isogram/src/lib.rs new file mode 100644 index 00000000..dd2012bf --- /dev/null +++ b/rust/isogram/src/lib.rs @@ -0,0 +1,28 @@ +use std::collections::HashSet; + +// pub fn check(candidate: &str) -> bool { +// let mut seen = HashSet::new(); +// for c in candidate.chars() { +// if c.is_alphabetic() { +// let c = c.to_ascii_lowercase(); +// if seen.contains(&c) { +// return false; +// } +// seen.insert(c); +// } +// } +// return true; +// } + +pub fn check(candidate: &str) -> bool { + let mut seen = HashSet::new(); + candidate.chars().for_each(|c| + if c.is_alphabetic() { + let c = c.to_ascii_lowercase(); + if seen.contains(&c) { + return false; + } + seen.insert(c); + }); + return true; +} \ No newline at end of file diff --git a/rust/isogram/tests/isogram.rs b/rust/isogram/tests/isogram.rs new file mode 100644 index 00000000..26048f58 --- /dev/null +++ b/rust/isogram/tests/isogram.rs @@ -0,0 +1,67 @@ +use isogram::check; + +#[test] +fn empty_string() { + assert!(check(""), "An empty string should be an isogram.") +} + +#[test] +fn only_lower_case_characters() { + assert!(check("isogram"), "\"isogram\" should be an isogram.") +} + +#[test] +fn one_duplicated_character() { + assert!( + !check("eleven"), + "\"eleven\" has more than one \'e\', therefore it is no isogram." + ) +} + +#[test] +fn longest_reported_english_isogram() { + assert!( + check("subdermatoglyphic"), + "\"subdermatoglyphic\" should be an isogram." + ) +} + +#[test] +fn one_duplicated_character_mixed_case() { + assert!( + !check("Alphabet"), + "\"Alphabet\" has more than one \'a\', therefore it is no isogram." + ) +} + +#[test] +fn hypothetical_isogramic_word_with_hyphen() { + assert!( + check("thumbscrew-japingly"), + "\"thumbscrew-japingly\" should be an isogram." + ) +} + +#[test] +fn isogram_with_duplicated_hyphen() { + assert!( + check("six-year-old"), + "\"six-year-old\" should be an isogram." + ) +} + +#[test] +fn made_up_name_that_is_an_isogram() { + assert!( + check("Emily Jung Schwartzkopf"), + "\"Emily Jung Schwartzkopf\" should be an isogram." + ) +} + +#[test] +fn duplicated_character_in_the_middle() { + assert!( + !check("accentor"), + "\"accentor\" has more than one \'c\', therefore it is no isogram." + ) +} diff --git a/rust/leap/.gitignore b/rust/leap/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/leap/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/leap/Cargo.toml b/rust/leap/Cargo.toml new file mode 100644 index 00000000..af783594 --- /dev/null +++ b/rust/leap/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "leap" +version = "1.6.0" diff --git a/rust/leap/HELP.md b/rust/leap/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/leap/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/leap/README.md b/rust/leap/README.md new file mode 100644 index 00000000..205b0885 --- /dev/null +++ b/rust/leap/README.md @@ -0,0 +1,72 @@ +# Leap + +Welcome to Leap on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Given a year, report if it is a leap year. + +The tricky thing here is that a leap year in the Gregorian calendar occurs: + +```text +on every year that is evenly divisible by 4 + except every year that is evenly divisible by 100 + unless the year is also evenly divisible by 400 +``` + +For example, 1997 is not a leap year, but 1996 is. 1900 is not a leap +year, but 2000 is. + +## Notes + +Though our exercise adopts some very simple rules, there is more to +learn! + +For a delightful, four minute explanation of the whole leap year +phenomenon, go watch [this youtube video][video]. + +[video]: http://www.youtube.com/watch?v=xX96xng7sAE + +You may use the [`arithmetic remainder` operator](https://doc.rust-lang.org/book/appendix-02-operators.html) to test for divisibility. + +## Source + +### Created by + +- @EduardoBautista + +### Contributed to by + +- @andrewclarkson +- @andy5995 +- @ashleygwilliams +- @ClashTheBunny +- @coriolinus +- @cwhakes +- @darnuria +- @EduardoBautista +- @efx +- @Emerentius +- @ErikSchierboom +- @hunger +- @IanWhitney +- @JIghtuse +- @jonasbn +- @kytrinyx +- @leoyvens +- @lutostag +- @mkantor +- @navossoc +- @nfiles +- @petertseng +- @pminten +- @rofrol +- @sshine +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +JavaRanch Cattle Drive, exercise 3 - http://www.javaranch.com/leap.jsp \ No newline at end of file diff --git a/rust/leap/src/lib.rs b/rust/leap/src/lib.rs new file mode 100644 index 00000000..1bac9f2a --- /dev/null +++ b/rust/leap/src/lib.rs @@ -0,0 +1,3 @@ +pub fn is_leap_year(year: u64) -> bool { + return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; +} diff --git a/rust/leap/tests/leap.rs b/rust/leap/tests/leap.rs new file mode 100644 index 00000000..d378797a --- /dev/null +++ b/rust/leap/tests/leap.rs @@ -0,0 +1,87 @@ +fn process_leapyear_case(year: u64, expected: bool) { + assert_eq!(leap::is_leap_year(year), expected); +} + +#[test] +fn test_year_not_divisible_by_4_common_year() { + process_leapyear_case(2015, false); +} + +#[test] +fn test_year_divisible_by_2_not_divisible_by_4_in_common_year() { + process_leapyear_case(1970, false); +} + +#[test] +fn test_year_divisible_by_4_not_divisible_by_100_leap_year() { + process_leapyear_case(1996, true); +} + +#[test] +fn test_year_divisible_by_4_and_5_is_still_a_leap_year() { + process_leapyear_case(1960, true); +} + +#[test] +fn test_year_divisible_by_100_not_divisible_by_400_common_year() { + process_leapyear_case(2100, false); +} + +#[test] +fn test_year_divisible_by_100_but_not_by_3_is_still_not_a_leap_year() { + process_leapyear_case(1900, false); +} + +#[test] +fn test_year_divisible_by_400_leap_year() { + process_leapyear_case(2000, true); +} + +#[test] +fn test_year_divisible_by_400_but_not_by_125_is_still_a_leap_year() { + process_leapyear_case(2400, true); +} + +#[test] +fn test_year_divisible_by_200_not_divisible_by_400_common_year() { + process_leapyear_case(1800, false); +} + +#[test] +fn test_any_old_year() { + process_leapyear_case(1997, false); +} + +#[test] +fn test_early_years() { + process_leapyear_case(1, false); + process_leapyear_case(4, true); + process_leapyear_case(100, false); + process_leapyear_case(400, true); + process_leapyear_case(900, false); +} + +#[test] +fn test_century() { + process_leapyear_case(1700, false); + process_leapyear_case(1800, false); + process_leapyear_case(1900, false); +} + +#[test] +fn test_exceptional_centuries() { + process_leapyear_case(1600, true); + process_leapyear_case(2000, true); + process_leapyear_case(2400, true); +} + +#[test] +fn test_years_1600_to_1699() { + let incorrect_years = (1600..1700) + .filter(|&year| leap::is_leap_year(year) != (year % 4 == 0)) + .collect::>(); + + if !incorrect_years.is_empty() { + panic!("incorrect result for years: {incorrect_years:?}"); + } +} diff --git a/rust/lucians-luscious-lasagna/.gitignore b/rust/lucians-luscious-lasagna/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/lucians-luscious-lasagna/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/lucians-luscious-lasagna/Cargo.toml b/rust/lucians-luscious-lasagna/Cargo.toml new file mode 100644 index 00000000..6f887665 --- /dev/null +++ b/rust/lucians-luscious-lasagna/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "lucians-luscious-lasagna" +version = "0.1.0" +edition = "2021" diff --git a/rust/lucians-luscious-lasagna/HELP.md b/rust/lucians-luscious-lasagna/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/lucians-luscious-lasagna/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/lucians-luscious-lasagna/HINTS.md b/rust/lucians-luscious-lasagna/HINTS.md new file mode 100644 index 00000000..5d1a5ff6 --- /dev/null +++ b/rust/lucians-luscious-lasagna/HINTS.md @@ -0,0 +1,31 @@ +# Hints + +## General + +- An integer literal can be defined as one or more consecutive digits. + +## 1. Define the expected oven time in minutes + +- You need to define a [function][functions] without any parameters. + +## 2. Calculate the remaining oven time in minutes + +- You need to define a [function][functions] with a single parameter. +- You can use and refer to the previously defined item by its name. +- The last expression in a function is [automatically returned][return-values] from the function; you don't have to explicitly indicate which value to return. +- You can use the [mathematical operator for subtraction][operators] to subtract values. + +## 3. Calculate the preparation time in minutes + +- You need to define a [function][functions] with a single parameter. +- You can use the [mathematical operator for multiplicaton][operators] to multiply values. + +## 4. Calculate the elapsed time in minutes + +- You need to define a [function][functions] with two parameters. +- You can [call][functions] one of the other functions you've defined previously. +- You can use the [mathematical operator for addition][operators] to add values. + +[functions]: https://doc.rust-lang.org/book/ch03-03-how-functions-work.html +[return-values]: https://doc.rust-lang.org/book/ch03-03-how-functions-work.html#functions-with-return-values +[operators]: https://doc.rust-lang.org/book/appendix-02-operators.html \ No newline at end of file diff --git a/rust/lucians-luscious-lasagna/README.md b/rust/lucians-luscious-lasagna/README.md new file mode 100644 index 00000000..4e40672c --- /dev/null +++ b/rust/lucians-luscious-lasagna/README.md @@ -0,0 +1,159 @@ +# Lucian's Luscious Lasagna + +Welcome to Lucian's Luscious Lasagna on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +In Rust, assigning a value to a name is referred to as a _binding_. Bindings are immutable unless declared with the `mut` keyword. As Rust is a statically-typed language, each binding has a type known at compile-time. + +Bindings are most commonly defined using the `let` keyword. Specifying a binding's type is optional for most bindings, as Rust's _type inference_ can usually infer the type based on their value. A binding looks like this: + +```rust +// Automatically inferred type +let fingers = 10; +``` + +Functions are _items_. Where bindings typically refer to a particular value, items refer to a unit of code organization, typically a function or a module, which is available throughout the lifetime of the program. A function automatically returns the result of its last expression. A function may have 0 or more parameters, which are bindings with a lifetime of the function call. + +Type inference is theoretically possible for functions, but is disabled as an intentional language design choice. While this means that you need to spend a little more time when writing code to specify precisely what a function's input and output types are, you save the time when you're reading the code, because all the input and output types are explicitly defined. + +```rust +fn add(x: i32, y: i32) -> i32 { + x + y +} +``` + +Invoking a function is done by specifying its name followed by parentheses. If the function requires parameters, an argument must be specified for each within the parentheses. + +```rust +let five = add(2, 3); +``` + +If a binding's type cannot be inferred, the compiler will report an error. To fix this, add an explicit type annotation to the binding. + +```rust +// Explicit type annotation +let fingers: i32 = 10; +``` + +Items in Rust can be used before or after they are defined, because they have a static lifetime. Bindings, on the other hand, can only be used _after_ they have been defined. Using a binding before it has been defined results in a compile error. + +```rust +fn main() { + // `fn add` hasn't yet been defined, but that's perfectly ok + dbg!(add(3, 4)); +} + +fn add(x: i32, y: i32) -> i32 { + x + y +} +``` + +```rust +// this won't compile; `a` is used before its binding is defined +let b = a; +let a = x + y; +``` + +Rust uses curly braces (`{}`) to define a scope. A binding defined within a scope can't escape from it. + +```rust +let a = 1; +dbg!(a); // 1 +{ + // Here, we re-bind `a` to a new value, which is still immutable. + // This technique is called _shadowing_. The new binding is constrained to + // this anonymous scope. Outside this scope, the previous binding still + // applies. + let a = 2; + let b = 3; + dbg!(a, b); // 2, 3 +} +// can't use `b` anymore because it is out of scope +// dbg!(b); + +// The shadowed `a` in the inner scope above has fallen out of scope, +// leaving us with our original binding. +dbg!(a); // 1 +``` + +Rust items are often organized in modules. Each crate is implicitly a module, but it can define inner sub-modules of arbitrary depth. A module groups related functionality and is defined using the `mod` keyword. + +```rust +mod calc_i32 { + fn add(a: i32, b: i32) -> i32 { a + b } + fn sub(a: i32, b: i32) -> i32 { a - b } + fn mul(a: i32, b: i32) -> i32 { a * b } + fn div(a: i32, b: i32) -> i32 { a / b } +} +``` + +Rust supports two types of comments. The keyword `//` indicates a single-line comment; everything following the keyword until the end of the line is ignored. The keywords `/*` and `*/` indicate a multi-line comment; everything within those two keywords is ignored. It is idiomatic and good practice to prefer single-line comments. + +Rust also supports doc-comments, which show up in the generated documentation produced by `cargo doc`. Outer doc comments are formed with the keyword `///`, which acts identically to the `//` keyword. They apply to the item which follows them, such as a function: + +```rust +/// The `add` function produces the sum of its arguments. +fn add(x: i32, y: i32) -> i32 { x + y } +``` + +Inner doc comments are formed with the keyword `//!`, which acts identically to the `//` keyword. They apply to the item enclosing them, such as a module: + +```rust +mod my_cool_module { + //! This module is the bee's knees. +} +``` + +Doc comments can be of arbitrary length and contain markdown, which is rendered into the generated documentation. + +## Instructions + +In this exercise you're going to write some code to help you cook a brilliant lasagna from your favorite cooking book. + +You have four tasks, all related to the time spent cooking the lasagna. + +## 1. Define the expected oven time in minutes + +Define the `expected_minutes_in_oven` binding to check how many minutes the lasagna should be in the oven. According to the cooking book, the expected oven time in minutes is 40: + +```rust +expected_minutes_in_oven() +// Returns: 40 +``` + +## 2. Calculate the remaining oven time in minutes + +Define the `remaining_minutes_in_oven` function that takes the actual minutes the lasagna has been in the oven as a parameter and returns how many minutes the lasagna still has to remain in the oven, based on the expected oven time in minutes from the previous task. + +```rust +remaining_minutes_in_oven(30) +// Returns: 10 +``` + +## 3. Calculate the preparation time in minutes + +Define the `preparation_time_in_minutes` function that takes the number of layers you added to the lasagna as a parameter and returns how many minutes you spent preparing the lasagna, assuming each layer takes you 2 minutes to prepare. + +```rust +preparation_time_in_minutes(2) +// Returns: 4 +``` + +## 4. Calculate the elapsed time in minutes + +Define the `elapsed_time_in_minutes` function that takes two parameters: the first parameter is the number of layers you added to the lasagna, and the second parameter is the number of minutes the lasagna has been in the oven. The function should return how many minutes you've worked on cooking the lasagna, which is the sum of the preparation time in minutes, and the time in minutes the lasagna has spent in the oven at the moment. + +```rust +elapsed_time_in_minutes(3, 20) +// Returns: 26 +``` + +## Source + +### Created by + +- @coriolinus +- @ErikSchierboom \ No newline at end of file diff --git a/rust/lucians-luscious-lasagna/src/lib.rs b/rust/lucians-luscious-lasagna/src/lib.rs new file mode 100644 index 00000000..64f36bf6 --- /dev/null +++ b/rust/lucians-luscious-lasagna/src/lib.rs @@ -0,0 +1,19 @@ +// This stub file contains items that aren't used yet; feel free to remove this module attribute +// to enable stricter warnings. +// #![allow(unused)] + +pub fn expected_minutes_in_oven() -> i32 { + return 40; +} + +pub fn remaining_minutes_in_oven(actual_minutes_in_oven: i32) -> i32 { + return expected_minutes_in_oven() - actual_minutes_in_oven; +} + +pub fn preparation_time_in_minutes(number_of_layers: i32) -> i32 { + return number_of_layers * 2; +} + +pub fn elapsed_time_in_minutes(number_of_layers: i32, actual_minutes_in_oven: i32) -> i32 { + return preparation_time_in_minutes(number_of_layers) + actual_minutes_in_oven; +} diff --git a/rust/lucians-luscious-lasagna/tests/lucians-luscious-lasagna.rs b/rust/lucians-luscious-lasagna/tests/lucians-luscious-lasagna.rs new file mode 100644 index 00000000..ea381d8c --- /dev/null +++ b/rust/lucians-luscious-lasagna/tests/lucians-luscious-lasagna.rs @@ -0,0 +1,39 @@ +use lucians_luscious_lasagna::{ + elapsed_time_in_minutes, expected_minutes_in_oven, preparation_time_in_minutes, + remaining_minutes_in_oven, +}; + +#[test] +fn expected_minutes_in_oven_is_correct() { + assert_eq!(40, expected_minutes_in_oven()); +} + +#[ignore] +#[test] +fn remaining_minutes_in_oven_after_fifteen_minutes() { + assert_eq!(15, remaining_minutes_in_oven(25)); +} + +#[ignore] +#[test] +fn preparation_time_in_minutes_for_one_layer() { + assert_eq!(2, preparation_time_in_minutes(1)); +} + +#[ignore] +#[test] +fn preparation_time_in_minutes_for_multiple_layers() { + assert_eq!(8, preparation_time_in_minutes(4)); +} + +#[ignore] +#[test] +fn elapsed_time_in_minutes_for_one_layer() { + assert_eq!(32, elapsed_time_in_minutes(1, 30)); +} + +#[ignore] +#[test] +fn elapsed_time_in_minutes_for_multiple_layers() { + assert_eq!(16, elapsed_time_in_minutes(4, 8)); +} diff --git a/rust/luhn/.gitignore b/rust/luhn/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/luhn/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/luhn/Cargo.toml b/rust/luhn/Cargo.toml new file mode 100644 index 00000000..2c401148 --- /dev/null +++ b/rust/luhn/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "luhn" +version = "1.6.1" diff --git a/rust/luhn/HELP.md b/rust/luhn/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/luhn/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/luhn/README.md b/rust/luhn/README.md new file mode 100644 index 00000000..9ca11c21 --- /dev/null +++ b/rust/luhn/README.md @@ -0,0 +1,102 @@ +# Luhn + +Welcome to Luhn on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Given a number determine whether or not it is valid per the Luhn formula. + +The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is +a simple checksum formula used to validate a variety of identification +numbers, such as credit card numbers and Canadian Social Insurance +Numbers. + +The task is to check if a given string is valid. + +Validating a Number +------ + +Strings of length 1 or less are not valid. Spaces are allowed in the input, +but they should be stripped before checking. All other non-digit characters +are disallowed. + +## Example 1: valid credit card number + +```text +4539 3195 0343 6467 +``` + +The first step of the Luhn algorithm is to double every second digit, +starting from the right. We will be doubling + +```text +4_3_ 3_9_ 0_4_ 6_6_ +``` + +If doubling the number results in a number greater than 9 then subtract 9 +from the product. The results of our doubling: + +```text +8569 6195 0383 3437 +``` + +Then sum all of the digits: + +```text +8+5+6+9+6+1+9+5+0+3+8+3+3+4+3+7 = 80 +``` + +If the sum is evenly divisible by 10, then the number is valid. This number is valid! + +## Example 2: invalid credit card number + +```text +8273 1232 7352 0569 +``` + +Double the second digits, starting from the right + +```text +7253 2262 5312 0539 +``` + +Sum the digits + +```text +7+2+5+3+2+2+6+2+5+3+1+2+0+5+3+9 = 57 +``` + +57 is not evenly divisible by 10, so this number is not valid. + +## Source + +### Created by + +- @IanWhitney + +### Contributed to by + +- @AvasDream +- @bitfield +- @coriolinus +- @cwhakes +- @efx +- @ErikSchierboom +- @gibfahn +- @idealhack +- @lutostag +- @mkantor +- @navossoc +- @nfiles +- @petertseng +- @rofrol +- @stkent +- @stringparser +- @workingjubilee +- @xakon +- @ZapAnton + +### Based on + +The Luhn Algorithm on Wikipedia - http://en.wikipedia.org/wiki/Luhn_algorithm \ No newline at end of file diff --git a/rust/luhn/src/lib.rs b/rust/luhn/src/lib.rs new file mode 100644 index 00000000..22a1fbd0 --- /dev/null +++ b/rust/luhn/src/lib.rs @@ -0,0 +1,23 @@ +/// Check a Luhn checksum. + +pub fn is_valid(code: &str) -> bool { + let mut sum = 0; + let mut i = 0; + for c in code.chars().rev() { + if c.is_whitespace() { + continue; + } + match c.to_digit(10) { + None => return false, + Some(d) if i % 2 == 0 => sum += d, + Some(d) => { + sum += 2 * d; + if 2 * d > 9 { + sum -= 9 + } + } + } + i += 1 + } + i > 1 && sum % 10 == 0 +} diff --git a/rust/luhn/tests/luhn.rs b/rust/luhn/tests/luhn.rs new file mode 100644 index 00000000..d6bbe1ab --- /dev/null +++ b/rust/luhn/tests/luhn.rs @@ -0,0 +1,121 @@ +use luhn::*; + +fn process_valid_case(number: &str, is_luhn_expected: bool) { + assert_eq!(is_valid(number), is_luhn_expected); +} + +#[test] +fn test_single_digit_strings_can_not_be_valid() { + process_valid_case("1", false); +} + +#[test] +fn test_a_single_zero_is_invalid() { + process_valid_case("0", false); +} + +#[test] +fn test_a_simple_valid_sin_that_remains_valid_if_reversed() { + process_valid_case("059", true); +} + +#[test] +fn test_a_simple_valid_sin_that_becomes_invalid_if_reversed() { + process_valid_case("59", true); +} + +#[test] +fn test_a_valid_canadian_sin() { + process_valid_case("055 444 285", true); +} + +#[test] +fn test_invalid_canadian_sin() { + process_valid_case("055 444 286", false); +} + +#[test] +fn test_invalid_credit_card() { + process_valid_case("8273 1232 7352 0569", false); +} + +#[test] +fn test_valid_number_with_an_even_number_of_digits() { + process_valid_case("095 245 88", true); +} + +#[test] +fn strings_that_contain_non_digits_are_invalid() { + process_valid_case("055a 444 285", false); +} + +#[test] +fn test_valid_strings_with_punctuation_included_become_invalid() { + process_valid_case("055-444-285", false); +} + +#[test] +fn symbols_are_invalid() { + process_valid_case("055£ 444$ 285", false); +} + +#[test] +fn test_single_zero_with_space_is_invalid() { + process_valid_case(" 0", false); +} + +#[test] +fn test_more_than_a_single_zero_is_valid() { + process_valid_case("0000 0", true); +} + +#[test] +fn test_input_digit_9_is_correctly_converted_to_output_digit_9() { + process_valid_case("091", true); +} + +#[test] +/// using ASCII value for doubled non-digit isn't allowed +/// Convert non-digits to their ASCII values and then offset them by 48 sometimes accidentally declare an invalid string to be valid. +/// This test is designed to avoid that solution. +fn test_using_ascii_value_for_doubled_nondigit_isnt_allowed() { + process_valid_case(":9", false); +} + +#[test] +/// valid strings with a non-digit added at the end become invalid +fn test_valid_strings_with_a_nondigit_added_at_the_end_become_invalid() { + process_valid_case("059a", false); +} + +#[test] +/// valid strings with symbols included become invalid +fn test_valid_strings_with_symbols_included_become_invalid() { + process_valid_case("055# 444$ 285", false); +} + +#[test] +/// using ASCII value for non-doubled non-digit isn't allowed +/// Convert non-digits to their ASCII values and then offset them by 48 sometimes accidentally declare an invalid string to be valid. +/// This test is designed to avoid that solution. +fn test_using_ascii_value_for_nondoubled_nondigit_isnt_allowed() { + process_valid_case("055b 444 285", false); +} + +#[test] +/// valid number with an odd number of spaces +fn test_valid_number_with_an_odd_number_of_spaces() { + process_valid_case("234 567 891 234", true); +} + +#[test] +/// non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed +fn test_invalid_char_in_middle_with_sum_divisible_by_10_isnt_allowed() { + process_valid_case("59%59", false); +} + +#[test] +/// unicode numeric characters are not allowed in a otherwise valid number +fn test_valid_strings_with_numeric_unicode_characters_become_invalid() { + process_valid_case("1249①", false); +} diff --git a/rust/macros/.gitignore b/rust/macros/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/macros/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/macros/Cargo.toml b/rust/macros/Cargo.toml new file mode 100644 index 00000000..8c82d2e6 --- /dev/null +++ b/rust/macros/Cargo.toml @@ -0,0 +1,6 @@ +[package] +edition = "2021" +name = "macros" +version = "0.1.0" + +[dependencies] diff --git a/rust/macros/HELP.md b/rust/macros/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/macros/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/macros/README.md b/rust/macros/README.md new file mode 100644 index 00000000..c1e60157 --- /dev/null +++ b/rust/macros/README.md @@ -0,0 +1,66 @@ +# Macros + +Welcome to Macros on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Macros are a powerful part of a Rust programmer's toolkit, and [macros by example](https://doc.rust-lang.org/reference/macros-by-example.html) are a relatively simple way to access this power. Let's write one! + +## Context + +What is a macro? [Wikipedia](https://en.wikipedia.org/wiki/Macro_(computer_science)) describes it thus: + +> A macro (short for "macroinstruction", from Greek μακρός 'long') in computer science is a rule or pattern that specifies how a certain input sequence (often a sequence of characters) should be mapped to a replacement output sequence (also often a sequence of characters) according to a defined procedure. The mapping process that instantiates (transforms) a macro use into a specific sequence is known as macro expansion. + +Illuminating! But to be more concrete, macros are a special syntax which allows you to generate code at compile time. Macros can be used for compile-time calculation, but more often they're just another way to abstract your code. For example, you've probably already used `println!()` and `vec![]`. These each take an arbitrary number of arguments, so you can't express them as simple functions. On the other hand, they always expand to some amount of absolutely standard Rust code. If you're interested, you can use the [cargo expand](https://github.com/dtolnay/cargo-expand) subcommand to view the results of macro expansion in your code. + +For further information about macros in Rust, The Rust Book has a [good chapter](https://doc.rust-lang.org/book/ch19-06-macros.html) on them. + +## Problem Statement + +You can produce a `Vec` of arbitrary length inline by using the `vec![]` macro. However, Rust doesn't come with a way to produce a [`HashMap`](https://doc.rust-lang.org/std/collections/struct.HashMap.html) inline. Rectify this by writing a `hashmap!()` macro. + +For example, a user of your library might write `hashmap!('a' => 3, 'b' => 11, 'z' => 32)`. This should expand to the following code: + +```rust +{ + let mut hm = HashMap::new(); + hm.insert('a', 3); + hm.insert('b', 11); + hm.insert('z', 32); + hm +} +``` + +Note that the [`maplit` crate](https://crates.io/crates/maplit) provides a macro which perfectly solves this exercise. Please implement your own solution instead of using this crate; please make an attempt on your own before viewing its source. + +Note that this exercise requires Rust 1.36 or later. + +## Source + +### Created by + +- @coriolinus + +### Contributed to by + +- @bantic +- @cwhakes +- @DarthStrom +- @efx +- @Emerentius +- @ErikSchierboom +- @lutostag +- @pedantic79 +- @petertseng +- @rofrol +- @ssomers +- @stringparser +- @tjade273 +- @xakon +- @ZapAnton + +### Based on + +Peter Goodspeed-Niklaus \ No newline at end of file diff --git a/rust/macros/src/lib.rs b/rust/macros/src/lib.rs new file mode 100644 index 00000000..78dc3c36 --- /dev/null +++ b/rust/macros/src/lib.rs @@ -0,0 +1,12 @@ +#[macro_export] +macro_rules! hashmap { + ($($($key:expr => $val:expr),+ $(,)?)? ) => { + { + let mut hm = ::std::collections::HashMap::new(); + $($( + hm.insert($key, $val); + )*)? + hm + } + }; +} diff --git a/rust/macros/tests/invalid/Cargo.toml b/rust/macros/tests/invalid/Cargo.toml new file mode 100644 index 00000000..824de562 --- /dev/null +++ b/rust/macros/tests/invalid/Cargo.toml @@ -0,0 +1,55 @@ +# +# This Cargo.toml file is used by the simple-trybuild module. +# When adding a new file, please name the [[bin]] name to match the file +# it is used to produce an error message +# + +[package] +name = "macros-tests" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies.macros] +path = "../../" +default-features = false + +[[bin]] +name = "comma-sep-rs" +path = "comma-sep.rs" + +[[bin]] +name = "double-commas-rs" +path = "double-commas.rs" + +[[bin]] +name = "only-arrow-rs" +path = "only-arrow.rs" + +[[bin]] +name = "only-comma-rs" +path = "only-comma.rs" + +[[bin]] +name = "single-argument-rs" +path = "single-argument.rs" + +[[bin]] +name = "triple-arguments-rs" +path = "triple-arguments.rs" + +[[bin]] +name = "two-arrows-rs" +path = "two-arrows.rs" + +[[bin]] +name = "leading-comma-rs" +path = "leading-comma.rs" + +[[bin]] +name = "no-comma-rs" +path = "no-comma.rs" + +[[bin]] +name = "missing-argument-rs" +path = "missing-argument.rs" diff --git a/rust/macros/tests/invalid/comma-sep.rs b/rust/macros/tests/invalid/comma-sep.rs new file mode 100644 index 00000000..c9b6f440 --- /dev/null +++ b/rust/macros/tests/invalid/comma-sep.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // using only commas is invalid + let _hm: HashMap<_, _> = hashmap!('a', 1); +} diff --git a/rust/macros/tests/invalid/double-commas.rs b/rust/macros/tests/invalid/double-commas.rs new file mode 100644 index 00000000..d52dec6a --- /dev/null +++ b/rust/macros/tests/invalid/double-commas.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // a single trailing comma is okay, but two is not + let _hm: HashMap<_, _> = hashmap!('a' => 2, ,); +} diff --git a/rust/macros/tests/invalid/leading-comma.rs b/rust/macros/tests/invalid/leading-comma.rs new file mode 100644 index 00000000..bc036669 --- /dev/null +++ b/rust/macros/tests/invalid/leading-comma.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // leading commas are not valid + let _hm: HashMap<_, _> = hashmap!(, 'a' => 2); +} diff --git a/rust/macros/tests/invalid/missing-argument.rs b/rust/macros/tests/invalid/missing-argument.rs new file mode 100644 index 00000000..893d609f --- /dev/null +++ b/rust/macros/tests/invalid/missing-argument.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // an argument should come between each pair of commas + let _hm: HashMap<_, _> = hashmap!('a' => 1, , 'b' => 2); +} diff --git a/rust/macros/tests/invalid/no-comma.rs b/rust/macros/tests/invalid/no-comma.rs new file mode 100644 index 00000000..c4e6a92b --- /dev/null +++ b/rust/macros/tests/invalid/no-comma.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // Key value pairs must be separated by commas + let _hm: HashMap<_, _> = hashmap!('a' => 1 'b' => 2); +} diff --git a/rust/macros/tests/invalid/only-arrow.rs b/rust/macros/tests/invalid/only-arrow.rs new file mode 100644 index 00000000..7f7d730b --- /dev/null +++ b/rust/macros/tests/invalid/only-arrow.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // a single random arrow is not valid + let _hm: HashMap<(), ()> = hashmap!(=>); +} diff --git a/rust/macros/tests/invalid/only-comma.rs b/rust/macros/tests/invalid/only-comma.rs new file mode 100644 index 00000000..3b863b3a --- /dev/null +++ b/rust/macros/tests/invalid/only-comma.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // a single random comma is not valid + let _hm: HashMap<(), ()> = hashmap!(,); +} diff --git a/rust/macros/tests/invalid/single-argument.rs b/rust/macros/tests/invalid/single-argument.rs new file mode 100644 index 00000000..f8ac092a --- /dev/null +++ b/rust/macros/tests/invalid/single-argument.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // a single argument is invalid + let _hm: HashMap<_, _> = hashmap!('a'); +} diff --git a/rust/macros/tests/invalid/triple-arguments.rs b/rust/macros/tests/invalid/triple-arguments.rs new file mode 100644 index 00000000..54351aef --- /dev/null +++ b/rust/macros/tests/invalid/triple-arguments.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // three arguments are invalid + hashmap!('a' => 1, 'b'); +} diff --git a/rust/macros/tests/invalid/two-arrows.rs b/rust/macros/tests/invalid/two-arrows.rs new file mode 100644 index 00000000..254a82e7 --- /dev/null +++ b/rust/macros/tests/invalid/two-arrows.rs @@ -0,0 +1,7 @@ +use macros::hashmap; +use std::collections::HashMap; + +fn main() { + // a trailing => isn't valid either + hashmap!('a' => 2, =>); +} diff --git a/rust/macros/tests/macros.rs b/rust/macros/tests/macros.rs new file mode 100644 index 00000000..741362ed --- /dev/null +++ b/rust/macros/tests/macros.rs @@ -0,0 +1,199 @@ +use macros::hashmap; +use std::collections::HashMap; + +#[test] +fn test_empty() { + let expected: HashMap = HashMap::new(); + let computed: HashMap = hashmap!(); + assert_eq!(computed, expected); +} + +#[test] +fn test_single() { + let mut expected = HashMap::new(); + expected.insert(1, "one"); + assert_eq!(hashmap!(1 => "one"), expected); +} + +#[test] +fn test_no_trailing_comma() { + let mut expected = HashMap::new(); + expected.insert(1, "one"); + expected.insert(2, "two"); + assert_eq!(hashmap!(1 => "one", 2 => "two"), expected); +} + +#[test] +fn test_trailing_comma() { + let mut expected = HashMap::new(); + expected.insert('h', 89); + expected.insert('a', 1); + expected.insert('s', 19); + expected.insert('h', 8); + assert_eq!( + hashmap!( + 'h' => 89, + 'a' => 1, + 's' => 19, + 'h' => 8, + ), + expected + ); +} + +#[test] +fn test_nested() { + let mut expected = HashMap::new(); + expected.insert("non-empty", { + let mut subhashmap = HashMap::new(); + subhashmap.insert(23, 623); + subhashmap.insert(34, 21); + subhashmap + }); + expected.insert("empty", HashMap::new()); + assert_eq!( + hashmap!( + "non-empty" => hashmap!( + 23 => 623, + 34 => 21 + ), + "empty" => hashmap!() + ), + expected + ); +} + +mod test { + #[test] + fn type_not_in_scope() { + use macros::hashmap; + + let _empty: ::std::collections::HashMap<(), ()> = hashmap!(); + let _without_comma = hashmap!(23=> 623, 34 => 21); + let _with_trailing = hashmap!(23 => 623, 34 => 21,); + } + + #[test] + fn test_macro_out_of_scope() { + let _empty: ::std::collections::HashMap<(), ()> = macros::hashmap!(); + let _without_comma = macros::hashmap!(23=> 623, 34 => 21); + let _with_trailing = macros::hashmap!(23 => 623, 34 => 21,); + } +} + +#[test] +fn test_type_override() { + // The macro should always use std::collections::HashMap and ignore crate::std::collections::HashMap + mod std { + pub mod collections { + pub struct HashMap; + + impl HashMap { + #[allow(dead_code)] + pub fn new() -> Self { + panic!("Do not allow users to override which HashMap is used"); + } + + #[allow(dead_code)] + pub fn insert(&mut self, _key: K, _val: V) { + panic!("Do not allow users to override which HashMap is used"); + } + } + } + } + + let _empty: ::std::collections::HashMap<(), ()> = hashmap!(); + let _without_comma = hashmap!(1 => 2, 3 => 4); + let _with_trailing = hashmap!(1 => 2, 3 => 4,); +} + +#[test] +fn test_compile_fails_comma_sep() { + simple_trybuild::compile_fail("comma-sep.rs"); +} + +#[test] +fn test_compile_fails_double_commas() { + simple_trybuild::compile_fail("double-commas.rs"); +} + +#[test] +fn test_compile_fails_only_comma() { + simple_trybuild::compile_fail("only-comma.rs"); +} + +#[test] +fn test_compile_fails_single_argument() { + simple_trybuild::compile_fail("single-argument.rs"); +} + +#[test] +fn test_compile_fails_triple_arguments() { + simple_trybuild::compile_fail("triple-arguments.rs"); +} + +#[test] +fn test_compile_fails_only_arrow() { + simple_trybuild::compile_fail("only-arrow.rs"); +} + +#[test] +fn test_compile_fails_two_arrows() { + simple_trybuild::compile_fail("two-arrows.rs"); +} + +#[test] +fn test_compile_fails_leading_comma() { + simple_trybuild::compile_fail("leading-comma.rs"); +} + +#[test] +fn test_compile_fails_no_comma() { + simple_trybuild::compile_fail("no-comma.rs"); +} + +#[test] +fn test_compile_fails_missing_argument() { + simple_trybuild::compile_fail("missing-argument.rs"); +} + +mod simple_trybuild { + use std::path::PathBuf; + use std::process::Command; + + pub fn compile_fail(file_name: &str) { + let invalid_path: PathBuf = ["tests", "invalid"].iter().collect::(); + + let mut file_path = invalid_path.clone(); + file_path.push(file_name); + assert!( + file_path.exists(), + "{:?} does not exist.", + file_path.into_os_string() + ); + + let test_name = file_name.replace('.', "-"); + let macros_dir = ["..", "..", "target", "tests", "macros"] + .iter() + .collect::(); + + let result = Command::new("cargo") + .current_dir(invalid_path) + .arg("build") + .arg("--offline") + .arg("--target-dir") + .arg(macros_dir) + .arg("--bin") + .arg(test_name) + .output(); + + if let Ok(result) = result { + assert!( + !result.status.success(), + "Expected {file_path:?} to fail to compile, but it succeeded." + ); + } else { + panic!("Running subprocess failed."); + } + } +} diff --git a/rust/matching-brackets/.gitignore b/rust/matching-brackets/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/matching-brackets/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/matching-brackets/Cargo.toml b/rust/matching-brackets/Cargo.toml new file mode 100644 index 00000000..55b42afb --- /dev/null +++ b/rust/matching-brackets/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "matching-brackets" +version = "2.0.0" diff --git a/rust/matching-brackets/HELP.md b/rust/matching-brackets/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/matching-brackets/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/matching-brackets/README.md b/rust/matching-brackets/README.md new file mode 100644 index 00000000..8d425409 --- /dev/null +++ b/rust/matching-brackets/README.md @@ -0,0 +1,40 @@ +# Matching Brackets + +Welcome to Matching Brackets on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Given a string containing brackets `[]`, braces `{}`, parentheses `()`, +or any combination thereof, verify that any and all pairs are matched +and nested correctly. + +## Source + +### Created by + +- @petertseng + +### Contributed to by + +- @ashleygwilliams +- @coriolinus +- @cwhakes +- @efx +- @Emerentius +- @ErikSchierboom +- @IanWhitney +- @ironhaven +- @lutostag +- @mkantor +- @nfiles +- @omer-g +- @petertseng +- @rofrol +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +Ginna Baker \ No newline at end of file diff --git a/rust/matching-brackets/src/lib.rs b/rust/matching-brackets/src/lib.rs new file mode 100644 index 00000000..02c807b8 --- /dev/null +++ b/rust/matching-brackets/src/lib.rs @@ -0,0 +1,21 @@ +use std::{collections::HashMap, iter::zip}; + +pub fn brackets_are_balanced(string: &str) -> bool { + let opening = ['(', '[', '{']; + let closing = [')', ']', '}']; + + let matching: HashMap = HashMap::from_iter(zip(closing, opening)); + let mut stack: Vec = vec![]; + for c in string.chars() { + if opening.contains(&c) { + stack.push(c) + } else if closing.contains(&c) { + if !stack.is_empty() && matching.get(&c).unwrap() == stack.last().unwrap() { + stack.pop(); + } else { + return false + } + } + } + stack.is_empty() +} diff --git a/rust/matching-brackets/tests/matching-brackets.rs b/rust/matching-brackets/tests/matching-brackets.rs new file mode 100644 index 00000000..0826dad1 --- /dev/null +++ b/rust/matching-brackets/tests/matching-brackets.rs @@ -0,0 +1,98 @@ +use matching_brackets::brackets_are_balanced; + +#[test] +fn paired_square_brackets() { + assert!(brackets_are_balanced("[]")); +} + +#[test] +fn empty_string() { + assert!(brackets_are_balanced("")); +} + +#[test] +fn unpaired_brackets() { + assert!(!brackets_are_balanced("[[")); +} + +#[test] +fn wrong_ordered_brackets() { + assert!(!brackets_are_balanced("}{")); +} + +#[test] +fn wrong_closing_bracket() { + assert!(!brackets_are_balanced("{]")); +} + +#[test] +fn paired_with_whitespace() { + assert!(brackets_are_balanced("{ }")); +} + +#[test] +fn partially_paired_brackets() { + assert!(!brackets_are_balanced("{[])")); +} + +#[test] +fn simple_nested_brackets() { + assert!(brackets_are_balanced("{[]}")); +} + +#[test] +fn several_paired_brackets() { + assert!(brackets_are_balanced("{}[]")); +} + +#[test] +fn paired_and_nested_brackets() { + assert!(brackets_are_balanced("([{}({}[])])")); +} + +#[test] +fn unopened_closing_brackets() { + assert!(!brackets_are_balanced("{[)][]}")); +} + +#[test] +fn unpaired_and_nested_brackets() { + assert!(!brackets_are_balanced("([{])")); +} + +#[test] +fn paired_and_wrong_nested_brackets() { + assert!(!brackets_are_balanced("[({]})")); +} + +#[test] +fn paired_and_incomplete_brackets() { + assert!(!brackets_are_balanced("{}[")); +} + +#[test] +fn too_many_closing_brackets() { + assert!(!brackets_are_balanced("[]]")); +} + +#[test] +fn early_incomplete_brackets() { + assert!(!brackets_are_balanced(")()")); +} + +#[test] +fn early_mismatched_brackets() { + assert!(!brackets_are_balanced("{)()")); +} + +#[test] +fn math_expression() { + assert!(brackets_are_balanced("(((185 + 223.85) * 15) - 543)/2")); +} + +#[test] +fn complex_latex_expression() { + let input = "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \ + \\end{array}\\right)"; + assert!(brackets_are_balanced(input)); +} diff --git a/rust/minesweeper/.gitignore b/rust/minesweeper/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/minesweeper/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/minesweeper/Cargo.toml b/rust/minesweeper/Cargo.toml new file mode 100644 index 00000000..5b53e264 --- /dev/null +++ b/rust/minesweeper/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "minesweeper" +version = "1.1.0" diff --git a/rust/minesweeper/HELP.md b/rust/minesweeper/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/minesweeper/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/minesweeper/README.md b/rust/minesweeper/README.md new file mode 100644 index 00000000..efca354d --- /dev/null +++ b/rust/minesweeper/README.md @@ -0,0 +1,80 @@ +# Minesweeper + +Welcome to Minesweeper on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Add the mine counts to a completed Minesweeper board. + +Minesweeper is a popular game where the user has to find the mines using +numeric hints that indicate how many mines are directly adjacent +(horizontally, vertically, diagonally) to a square. + +In this exercise you have to create some code that counts the number of +mines adjacent to a given empty square and replaces that square with the +count. + +The board is a rectangle composed of blank space (' ') characters. A mine +is represented by an asterisk ('\*') character. + +If a given space has no adjacent mines at all, leave that square blank. + +## Examples + +For example you may receive a 5 x 4 board like this (empty spaces are +represented here with the '·' character for display on screen): + +``` +·*·*· +··*·· +··*·· +····· +``` + +And your code will transform it into this: + +``` +1*3*1 +13*31 +·2*2· +·111· +``` + +## Performance Hint + +All the inputs and outputs are in ASCII. Rust `String`s and `&str` are utf8, +so while one might expect "Hello".chars() to be simple, it actually has to +check each char to see if it's 1, 2, 3 or 4 `u8`s long. If we know a `&str` +is ASCII then we can call `.as_bytes()` and refer to the underlying data via a `&[u8]` slice. +Iterating over a u8 slice of ASCII is much quicker as there are no codepoints +involved - every ASCII char is one u8 long. + +Can you complete the challenge without cloning the input? + +## Source + +### Created by + +- @EduardoBautista + +### Contributed to by + +- @ashleygwilliams +- @coriolinus +- @cwhakes +- @EduardoBautista +- @efx +- @ErikSchierboom +- @ffflorian +- @IanWhitney +- @kytrinyx +- @lutostag +- @mkantor +- @nfiles +- @petertseng +- @rofrol +- @stringparser +- @workingjubilee +- @xakon +- @ZapAnton \ No newline at end of file diff --git a/rust/minesweeper/src/lib.rs b/rust/minesweeper/src/lib.rs new file mode 100644 index 00000000..69d939f6 --- /dev/null +++ b/rust/minesweeper/src/lib.rs @@ -0,0 +1,68 @@ +use std::{char::from_digit, cmp, collections::HashMap}; + +pub fn annotate(minefield: &[&str]) -> Vec { + let neighborhood = find_neighborhoods(minefield); + minefield + .iter() + .enumerate() + .map(|(i, row)| { + row.chars() + .enumerate() + .map(|(j, c)| { + if c == '*' { + '*' + } else { + let neighbor_count = *neighborhood.get(&(i, j)).unwrap_or(&0); + if neighbor_count == 0 { + ' ' + } else { + from_digit(neighbor_count, 10).unwrap() + } + } + }) + .collect() + }) + .collect() +} + +fn find_neighborhoods(minefield: &[&str]) -> HashMap<(usize, usize), u32> { + let mut neighbors: HashMap<(usize, usize), u32> = HashMap::new(); + for (i, row) in minefield.iter().enumerate() { + for (j, c) in row.chars().enumerate() { + if c == '*' { + // Row above + if i > 0 { + for k in j.saturating_sub(1)..=cmp::min(j + 1, row.len() - 1) { + neighbors + .entry((i - 1, k)) + .and_modify(|c: &mut u32| *c += 1) + .or_insert(1); + } + } + // Current row + if j > 0 { + neighbors + .entry((i, j - 1)) + .and_modify(|c| *c += 1) + .or_insert(1); + } + if j < row.len() - 1 { + neighbors + .entry((i, j + 1)) + .and_modify(|c| *c += 1) + .or_insert(1); + } + // Row below + if i < minefield.len() - 1 { + for k in j.saturating_sub(1)..=cmp::min(j + 1, row.len() - 1) { + neighbors + .entry((i + 1, k)) + .and_modify(|c: &mut u32| *c += 1) + .or_insert(1); + } + } + } + } + } + neighbors +} diff --git a/rust/minesweeper/tests/minesweeper.rs b/rust/minesweeper/tests/minesweeper.rs new file mode 100644 index 00000000..c27bbd5c --- /dev/null +++ b/rust/minesweeper/tests/minesweeper.rs @@ -0,0 +1,141 @@ +use minesweeper::annotate; + +fn remove_annotations(board: &[&str]) -> Vec { + board.iter().map(|r| remove_annotations_in_row(r)).collect() +} + +fn remove_annotations_in_row(row: &str) -> String { + row.chars() + .map(|ch| match ch { + '*' => '*', + _ => ' ', + }) + .collect() +} + +fn run_test(test_case: &[&str]) { + let cleaned = remove_annotations(test_case); + let cleaned_strs = cleaned.iter().map(|r| &r[..]).collect::>(); + let expected = test_case.iter().map(|&r| r.to_string()).collect::>(); + assert_eq!(expected, annotate(&cleaned_strs)); +} + +#[test] +fn no_rows() { + #[rustfmt::skip] + run_test(&[ + ]); +} + +#[test] +fn no_columns() { + #[rustfmt::skip] + run_test(&[ + "", + ]); +} + +#[test] +fn no_mines() { + #[rustfmt::skip] + run_test(&[ + " ", + " ", + " ", + ]); +} + +#[test] +fn board_with_only_mines() { + #[rustfmt::skip] + run_test(&[ + "***", + "***", + "***", + ]); +} + +#[test] +fn mine_surrounded_by_spaces() { + #[rustfmt::skip] + run_test(&[ + "111", + "1*1", + "111", + ]); +} + +#[test] +fn space_surrounded_by_mines() { + #[rustfmt::skip] + run_test(&[ + "***", + "*8*", + "***", + ]); +} + +#[test] +fn horizontal_line() { + #[rustfmt::skip] + run_test(&[ + "1*2*1", + ]); +} + +#[test] +fn horizontal_line_mines_at_edges() { + #[rustfmt::skip] + run_test(&[ + "*1 1*", + ]); +} + +#[test] +fn vertical_line() { + #[rustfmt::skip] + run_test(&[ + "1", + "*", + "2", + "*", + "1", + ]); +} + +#[test] +fn vertical_line_mines_at_edges() { + #[rustfmt::skip] + run_test(&[ + "*", + "1", + " ", + "1", + "*", + ]); +} + +#[test] +fn cross() { + #[rustfmt::skip] + run_test(&[ + " 2*2 ", + "25*52", + "*****", + "25*52", + " 2*2 ", + ]); +} + +#[test] +fn large_board() { + #[rustfmt::skip] + run_test(&[ + "1*22*1", + "12*322", + " 123*2", + "112*4*", + "1*22*2", + "111111", + ]); +} diff --git a/rust/nucleotide-count/.gitignore b/rust/nucleotide-count/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/nucleotide-count/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/nucleotide-count/Cargo.toml b/rust/nucleotide-count/Cargo.toml new file mode 100644 index 00000000..97d8007e --- /dev/null +++ b/rust/nucleotide-count/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "nucleotide-count" +version = "1.3.0" diff --git a/rust/nucleotide-count/HELP.md b/rust/nucleotide-count/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/nucleotide-count/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/nucleotide-count/README.md b/rust/nucleotide-count/README.md new file mode 100644 index 00000000..fd9aa542 --- /dev/null +++ b/rust/nucleotide-count/README.md @@ -0,0 +1,59 @@ +# Nucleotide Count + +Welcome to Nucleotide Count on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Each of us inherits from our biological parents a set of chemical instructions known as DNA that influence how our bodies are constructed. All known life depends on DNA! + +> Note: You do not need to understand anything about nucleotides or DNA to complete this exercise. + +DNA is a long chain of other chemicals and the most important are the four nucleotides, adenine, cytosine, guanine and thymine. A single DNA chain can contain billions of these four nucleotides and the order in which they occur is important! +We call the order of these nucleotides in a bit of DNA a "DNA sequence". + +We represent a DNA sequence as an ordered collection of these four nucleotides and a common way to do that is with a string of characters such as "ATTACG" for a DNA sequence of 6 nucleotides. +'A' for adenine, 'C' for cytosine, 'G' for guanine, and 'T' for thymine. + +Given a string representing a DNA sequence, count how many of each nucleotide is present. +If the string contains characters that aren't A, C, G, or T then it is invalid and you should signal an error. + +For example: + +``` +"GATTACA" -> 'A': 3, 'C': 1, 'G': 1, 'T': 2 +"INVALID" -> error +``` + +## Source + +### Created by + +- @EduardoBautista + +### Contributed to by + +- @andrewclarkson +- @ashleygwilliams +- @cmcaine +- @coriolinus +- @cwhakes +- @Dysp +- @EduardoBautista +- @efx +- @ErikSchierboom +- @IanWhitney +- @kytrinyx +- @lutostag +- @mkantor +- @nfiles +- @petertseng +- @pminten +- @rofrol +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +The Calculating DNA Nucleotides_problem at Rosalind - http://rosalind.info/problems/dna/ \ No newline at end of file diff --git a/rust/nucleotide-count/src/lib.rs b/rust/nucleotide-count/src/lib.rs new file mode 100644 index 00000000..bfecccd8 --- /dev/null +++ b/rust/nucleotide-count/src/lib.rs @@ -0,0 +1,22 @@ +use std::collections::HashMap; + +pub fn count(nucleotide: char, dna: &str) -> Result { + nucleotide_counts(dna).and_then(|h| { + match h.get(&nucleotide) { + None => Err(nucleotide), + Some(c) => Ok(*c) + } + }) +} + +pub fn nucleotide_counts(dna: &str) -> Result, char> { + let mut count: HashMap = HashMap::from([('A', 0), ('C', 0), ('G', 0), ('T', 0)]); + for c in dna.chars() { + if count.contains_key(&c) { + count.entry(c).and_modify(|n| *n += 1); + } else { + return Err(c); + } + } + Ok(count) +} diff --git a/rust/nucleotide-count/tests/nucleotide-count.rs b/rust/nucleotide-count/tests/nucleotide-count.rs new file mode 100644 index 00000000..81905808 --- /dev/null +++ b/rust/nucleotide-count/tests/nucleotide-count.rs @@ -0,0 +1,88 @@ +use nucleotide_count as dna; + +use std::collections::HashMap; + +fn process_nucleotidecounts_case(s: &str, pairs: &[(char, usize)]) { + // The reason for the awkward code in here is to ensure that the failure + // message for assert_eq! is as informative as possible. A simpler + // solution would simply check the length of the map, and then + // check for the presence and value of each key in the given pairs vector. + let mut m: HashMap = dna::nucleotide_counts(s).unwrap(); + for &(k, v) in pairs.iter() { + assert_eq!((k, m.remove(&k)), (k, Some(v))); + } + + // may fail with a message that clearly shows all extra pairs in the map + assert_eq!(m.iter().collect::>(), vec![]); +} + +#[test] +fn count_returns_result() { + assert!(dna::count('A', "").is_ok()); +} + +#[test] +fn test_count_empty() { + assert_eq!(dna::count('A', ""), Ok(0)); +} + +#[test] +fn count_invalid_nucleotide() { + assert_eq!(dna::count('X', "A"), Err('X')); +} + +#[test] +fn count_invalid_dna() { + assert_eq!(dna::count('A', "AX"), Err('X')); +} + +#[test] +fn test_count_repetitive_cytosine() { + assert_eq!(dna::count('C', "CCCCC"), Ok(5)); +} + +#[test] +fn test_count_only_thymine() { + assert_eq!(dna::count('T', "GGGGGTAACCCGG"), Ok(1)); +} + +#[test] +fn counts_returns_result() { + assert!(dna::nucleotide_counts("ACGT").is_ok()); +} + +#[test] +fn test_empty_strand() { + process_nucleotidecounts_case("", &[('A', 0), ('T', 0), ('C', 0), ('G', 0)]); +} + +#[test] +/// can count one nucleotide in single-character input +fn test_can_count_one_nucleotide_in_singlecharacter_input() { + process_nucleotidecounts_case("G", &[('A', 0), ('C', 0), ('G', 1), ('T', 0)]); +} + +#[test] +fn test_strand_with_repeated_nucleotide() { + process_nucleotidecounts_case("GGGGGGG", &[('A', 0), ('T', 0), ('C', 0), ('G', 7)]); +} + +#[test] +/// strand with multiple nucleotides +fn test_strand_with_multiple_nucleotides() { + process_nucleotidecounts_case( + "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC", + &[('A', 20), ('T', 21), ('C', 12), ('G', 17)], + ); +} + +#[test] +fn counts_invalid_nucleotide_results_in_err() { + assert_eq!(dna::nucleotide_counts("GGXXX"), Err('X')); +} + +#[test] +/// strand with invalid nucleotides +fn test_strand_with_invalid_nucleotides() { + assert_eq!(dna::nucleotide_counts("AGXXACT"), Err('X'),); +} diff --git a/rust/parallel-letter-frequency/.gitignore b/rust/parallel-letter-frequency/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/parallel-letter-frequency/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/parallel-letter-frequency/Cargo.toml b/rust/parallel-letter-frequency/Cargo.toml new file mode 100644 index 00000000..6b0de0ca --- /dev/null +++ b/rust/parallel-letter-frequency/Cargo.toml @@ -0,0 +1,7 @@ +[package] +edition = "2021" +name = "parallel-letter-frequency" +version = "0.0.0" + +[dependencies] +rayon = "1.5.1" \ No newline at end of file diff --git a/rust/parallel-letter-frequency/HELP.md b/rust/parallel-letter-frequency/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/parallel-letter-frequency/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/parallel-letter-frequency/README.md b/rust/parallel-letter-frequency/README.md new file mode 100644 index 00000000..3bea9272 --- /dev/null +++ b/rust/parallel-letter-frequency/README.md @@ -0,0 +1,75 @@ +# Parallel Letter Frequency + +Welcome to Parallel Letter Frequency on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Count the frequency of letters in texts using parallel computation. + +Parallelism is about doing things in parallel that can also be done +sequentially. A common example is counting the frequency of letters. +Create a function that returns the total frequency of each letter in a +list of texts and that employs parallelism. + +Learn more about concurrency in Rust here: + +- [Concurrency](https://doc.rust-lang.org/book/ch16-00-concurrency.html) + +## Bonus + +This exercise also includes a benchmark, with a sequential implementation as a +baseline. You can compare your solution to the benchmark. Observe the +effect different size inputs have on the performance of each. Can you +surpass the benchmark using concurrent programming techniques? + +As of this writing, test::Bencher is unstable and only available on +*nightly* Rust. Run the benchmarks with Cargo: + +``` +cargo bench +``` + +If you are using rustup.rs: + +``` +rustup run nightly cargo bench +``` + +- [Benchmark tests](https://doc.rust-lang.org/stable/unstable-book/library-features/test.html) + +Learn more about nightly Rust: + +- [Nightly Rust](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html) +- [Installing Rust nightly](https://rust-lang.github.io/rustup/concepts/channels.html#working-with-nightly-rust) + +## Source + +### Created by + +- @EduardoBautista + +### Contributed to by + +- @andrewclarkson +- @ashleygwilliams +- @ccouzens +- @ClashTheBunny +- @coriolinus +- @cwhakes +- @EduardoBautista +- @efx +- @ErikSchierboom +- @etrepum +- @glennpratt +- @IanWhitney +- @kytrinyx +- @lutostag +- @mkantor +- @nfiles +- @petertseng +- @rofrol +- @sjwarner-bp +- @stringparser +- @xakon +- @ZapAnton \ No newline at end of file diff --git a/rust/parallel-letter-frequency/benches/benchmark.rs b/rust/parallel-letter-frequency/benches/benchmark.rs new file mode 100644 index 00000000..0e5bbee2 --- /dev/null +++ b/rust/parallel-letter-frequency/benches/benchmark.rs @@ -0,0 +1,102 @@ +#![feature(test)] +extern crate parallel_letter_frequency; +extern crate test; + +use std::collections::HashMap; +use test::Bencher; + +#[bench] +fn bench_tiny_parallel(b: &mut Bencher) { + let tiny = &["a"]; + b.iter(|| parallel_letter_frequency::frequency(tiny, 3)); +} + +#[bench] +fn bench_tiny_sequential(b: &mut Bencher) { + let tiny = &["a"]; + b.iter(|| frequency(tiny)); +} + +#[bench] +fn bench_small_parallel(b: &mut Bencher) { + let texts = all_texts(1); + b.iter(|| parallel_letter_frequency::frequency(&texts, 3)); +} + +#[bench] +fn bench_small_sequential(b: &mut Bencher) { + let texts = all_texts(1); + b.iter(|| frequency(&texts)); +} + +#[bench] +fn bench_large_parallel(b: &mut Bencher) { + let texts = all_texts(30); + b.iter(|| parallel_letter_frequency::frequency(&texts, 3)); +} + +#[bench] +fn bench_large_sequential(b: &mut Bencher) { + let texts = all_texts(30); + b.iter(|| frequency(&texts)); +} + +/// Simple sequential char frequency. Can it be beat? +pub fn frequency(texts: &[&str]) -> HashMap { + let mut map = HashMap::new(); + + for line in texts { + for chr in line.chars().filter(|c| c.is_alphabetic()) { + if let Some(c) = chr.to_lowercase().next() { + (*map.entry(c).or_insert(0)) += 1; + } + } + } + + map +} + +fn all_texts(repeat: usize) -> Vec<&'static str> { + [ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER] + .iter() + .cycle() + .take(3 * repeat) + .flat_map(|anthem| anthem.iter().cloned()) + .collect() +} + +// Poem by Friedrich Schiller. The corresponding music is the European Anthem. +pub const ODE_AN_DIE_FREUDE: [&str; 8] = [ + "Freude schöner Götterfunken", + "Tochter aus Elysium,", + "Wir betreten feuertrunken,", + "Himmlische, dein Heiligtum!", + "Deine Zauber binden wieder", + "Was die Mode streng geteilt;", + "Alle Menschen werden Brüder,", + "Wo dein sanfter Flügel weilt.", +]; + +// Dutch national anthem +pub const WILHELMUS: [&str; 8] = [ + "Wilhelmus van Nassouwe", + "ben ik, van Duitsen bloed,", + "den vaderland getrouwe", + "blijf ik tot in den dood.", + "Een Prinse van Oranje", + "ben ik, vrij, onverveerd,", + "den Koning van Hispanje", + "heb ik altijd geëerd.", +]; + +// American national anthem +pub const STAR_SPANGLED_BANNER: [&str; 8] = [ + "O say can you see by the dawn's early light,", + "What so proudly we hailed at the twilight's last gleaming,", + "Whose broad stripes and bright stars through the perilous fight,", + "O'er the ramparts we watched, were so gallantly streaming?", + "And the rockets' red glare, the bombs bursting in air,", + "Gave proof through the night that our flag was still there;", + "O say does that star-spangled banner yet wave,", + "O'er the land of the free and the home of the brave?", +]; diff --git a/rust/parallel-letter-frequency/src/lib.rs b/rust/parallel-letter-frequency/src/lib.rs new file mode 100644 index 00000000..17075d14 --- /dev/null +++ b/rust/parallel-letter-frequency/src/lib.rs @@ -0,0 +1,29 @@ +use std::collections::HashMap; +use rayon::prelude::*; + +pub fn seq_frequency(text: &str) -> HashMap { + let mut map = HashMap::new(); + + for chr in text.chars().filter(|c| c.is_alphabetic()) { + if let Some(c) = chr.to_lowercase().next() { + (*map.entry(c).or_insert(0)) += 1; + } + } + + map +} + +pub fn frequency(input: &[&str], _worker_count: usize) -> HashMap { + let maps: Vec<_> = input.par_iter() + .map(|s| seq_frequency(s)) + .collect(); + + let mut result: HashMap = HashMap::new(); + for map in maps { + for (k, v) in map { + *result.entry(k).or_insert(0) += v; + } + } + + result +} \ No newline at end of file diff --git a/rust/parallel-letter-frequency/tests/parallel-letter-frequency.rs b/rust/parallel-letter-frequency/tests/parallel-letter-frequency.rs new file mode 100644 index 00000000..36c6c341 --- /dev/null +++ b/rust/parallel-letter-frequency/tests/parallel-letter-frequency.rs @@ -0,0 +1,122 @@ +use std::collections::HashMap; + +use parallel_letter_frequency as frequency; + +// Poem by Friedrich Schiller. The corresponding music is the European Anthem. +const ODE_AN_DIE_FREUDE: [&str; 8] = [ + "Freude schöner Götterfunken", + "Tochter aus Elysium,", + "Wir betreten feuertrunken,", + "Himmlische, dein Heiligtum!", + "Deine Zauber binden wieder", + "Was die Mode streng geteilt;", + "Alle Menschen werden Brüder,", + "Wo dein sanfter Flügel weilt.", +]; + +// Dutch national anthem +const WILHELMUS: [&str; 8] = [ + "Wilhelmus van Nassouwe", + "ben ik, van Duitsen bloed,", + "den vaderland getrouwe", + "blijf ik tot in den dood.", + "Een Prinse van Oranje", + "ben ik, vrij, onverveerd,", + "den Koning van Hispanje", + "heb ik altijd geëerd.", +]; + +// American national anthem +const STAR_SPANGLED_BANNER: [&str; 8] = [ + "O say can you see by the dawn's early light,", + "What so proudly we hailed at the twilight's last gleaming,", + "Whose broad stripes and bright stars through the perilous fight,", + "O'er the ramparts we watched, were so gallantly streaming?", + "And the rockets' red glare, the bombs bursting in air,", + "Gave proof through the night that our flag was still there;", + "O say does that star-spangled banner yet wave,", + "O'er the land of the free and the home of the brave?", +]; + +#[test] +fn test_no_texts() { + assert_eq!(frequency::frequency(&[], 4), HashMap::new()); +} + +#[test] +fn test_one_letter() { + let mut hm = HashMap::new(); + hm.insert('a', 1); + assert_eq!(frequency::frequency(&["a"], 4), hm); +} + +#[test] +fn test_case_insensitivity() { + let mut hm = HashMap::new(); + hm.insert('a', 2); + assert_eq!(frequency::frequency(&["aA"], 4), hm); +} + +#[test] +fn test_many_empty_lines() { + let v = vec![""; 1000]; + assert_eq!(frequency::frequency(&v[..], 4), HashMap::new()); +} + +#[test] +fn test_many_times_same_text() { + let v = vec!["abc"; 1000]; + let mut hm = HashMap::new(); + hm.insert('a', 1000); + hm.insert('b', 1000); + hm.insert('c', 1000); + assert_eq!(frequency::frequency(&v[..], 4), hm); +} + +#[test] +fn test_punctuation_doesnt_count() { + assert!(!frequency::frequency(&WILHELMUS, 4).contains_key(&',')); +} + +#[test] +fn test_numbers_dont_count() { + assert!(!frequency::frequency(&["Testing, 1, 2, 3"], 4).contains_key(&'1')); +} + +#[test] +fn test_all_three_anthems_1_worker() { + let mut v = Vec::new(); + for anthem in [ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER].iter() { + for line in anthem.iter() { + v.push(*line); + } + } + let freqs = frequency::frequency(&v[..], 1); + assert_eq!(freqs.get(&'a'), Some(&49)); + assert_eq!(freqs.get(&'t'), Some(&56)); + assert_eq!(freqs.get(&'ü'), Some(&2)); +} + +#[test] +fn test_all_three_anthems_3_workers() { + let mut v = Vec::new(); + for anthem in [ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER].iter() { + for line in anthem.iter() { + v.push(*line); + } + } + let freqs = frequency::frequency(&v[..], 3); + assert_eq!(freqs.get(&'a'), Some(&49)); + assert_eq!(freqs.get(&'t'), Some(&56)); + assert_eq!(freqs.get(&'ü'), Some(&2)); +} + +#[test] +fn test_non_integer_multiple_of_threads() { + let v = vec!["abc"; 999]; + let mut hm = HashMap::new(); + hm.insert('a', 999); + hm.insert('b', 999); + hm.insert('c', 999); + assert_eq!(frequency::frequency(&v[..], 4), hm); +} diff --git a/rust/raindrops/.gitignore b/rust/raindrops/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/raindrops/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/raindrops/Cargo.toml b/rust/raindrops/Cargo.toml new file mode 100644 index 00000000..874f0551 --- /dev/null +++ b/rust/raindrops/Cargo.toml @@ -0,0 +1,7 @@ +[package] +edition = "2021" +name = "raindrops" +version = "1.1.0" + +[dependencies] +string-builder = "0.2.0" \ No newline at end of file diff --git a/rust/raindrops/HELP.md b/rust/raindrops/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/raindrops/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/raindrops/README.md b/rust/raindrops/README.md new file mode 100644 index 00000000..f381f737 --- /dev/null +++ b/rust/raindrops/README.md @@ -0,0 +1,54 @@ +# Raindrops + +Welcome to Raindrops on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Your task is to convert a number into a string that contains raindrop sounds corresponding to certain potential factors. A factor is a number that evenly divides into another number, leaving no remainder. The simplest way to test if a one number is a factor of another is to use the [modulo operation](https://en.wikipedia.org/wiki/Modulo_operation). + +The rules of `raindrops` are that if a given number: + +- has 3 as a factor, add 'Pling' to the result. +- has 5 as a factor, add 'Plang' to the result. +- has 7 as a factor, add 'Plong' to the result. +- _does not_ have any of 3, 5, or 7 as a factor, the result should be the digits of the number. + +## Examples + +- 28 has 7 as a factor, but not 3 or 5, so the result would be "Plong". +- 30 has both 3 and 5 as factors, but not 7, so the result would be "PlingPlang". +- 34 is not factored by 3, 5, or 7, so the result would be "34". + +## Source + +### Created by + +- @EduardoBautista + +### Contributed to by + +- @ashleygwilliams +- @ClashTheBunny +- @coriolinus +- @cwhakes +- @eddyp +- @EduardoBautista +- @efx +- @ErikSchierboom +- @IanWhitney +- @kytrinyx +- @leoyvens +- @lutostag +- @mkantor +- @nfiles +- @petertseng +- @rofrol +- @stevejb71 +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +A variation on FizzBuzz, a famous technical interview question that is intended to weed out potential candidates. That question is itself derived from Fizz Buzz, a popular children's game for teaching division. - https://en.wikipedia.org/wiki/Fizz_buzz \ No newline at end of file diff --git a/rust/raindrops/src/lib.rs b/rust/raindrops/src/lib.rs new file mode 100644 index 00000000..09e7816f --- /dev/null +++ b/rust/raindrops/src/lib.rs @@ -0,0 +1,17 @@ +use std::collections::HashMap; + +use string_builder::Builder; + +pub fn raindrops(n: u32) -> String { + let sounds = HashMap::from([(3, "Pling"), (5, "Plang"), (7, "Plong")]); + let mut s = Builder::default(); + for (i, snd) in sounds { + if n % i == 0 { + s.append(snd); + } + } + if s.len() == 0 { + s.append(n.to_string()); + } + return s.string().unwrap(); +} diff --git a/rust/raindrops/tests/raindrops.rs b/rust/raindrops/tests/raindrops.rs new file mode 100644 index 00000000..48558af1 --- /dev/null +++ b/rust/raindrops/tests/raindrops.rs @@ -0,0 +1,94 @@ +#[test] +fn test_1() { + assert_eq!("1", raindrops::raindrops(1)); +} + +#[test] +fn test_3() { + assert_eq!("Pling", raindrops::raindrops(3)); +} + +#[test] +fn test_5() { + assert_eq!("Plang", raindrops::raindrops(5)); +} + +#[test] +fn test_7() { + assert_eq!("Plong", raindrops::raindrops(7)); +} + +#[test] +fn test_6() { + assert_eq!("Pling", raindrops::raindrops(6)); +} + +#[test] +fn test_8() { + assert_eq!("8", raindrops::raindrops(8)); +} + +#[test] +fn test_9() { + assert_eq!("Pling", raindrops::raindrops(9)); +} + +#[test] +fn test_10() { + assert_eq!("Plang", raindrops::raindrops(10)); +} + +#[test] +fn test_14() { + assert_eq!("Plong", raindrops::raindrops(14)); +} + +#[test] +fn test_15() { + assert_eq!("PlingPlang", raindrops::raindrops(15)); +} + +#[test] +fn test_21() { + assert_eq!("PlingPlong", raindrops::raindrops(21)); +} + +#[test] +fn test_25() { + assert_eq!("Plang", raindrops::raindrops(25)); +} + +#[test] +fn test_27() { + assert_eq!("Pling", raindrops::raindrops(27)); +} + +#[test] +fn test_35() { + assert_eq!("PlangPlong", raindrops::raindrops(35)); +} + +#[test] +fn test_49() { + assert_eq!("Plong", raindrops::raindrops(49)); +} + +#[test] +fn test_52() { + assert_eq!("52", raindrops::raindrops(52)); +} + +#[test] +fn test_105() { + assert_eq!("PlingPlangPlong", raindrops::raindrops(105)); +} + +#[test] +fn test_3125() { + assert_eq!("Plang", raindrops::raindrops(3125)); +} + +#[test] +fn test_12121() { + assert_eq!("12121", raindrops::raindrops(12_121)); +} diff --git a/rust/reverse-string/.gitignore b/rust/reverse-string/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/reverse-string/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/reverse-string/Cargo.toml b/rust/reverse-string/Cargo.toml new file mode 100644 index 00000000..1f894d50 --- /dev/null +++ b/rust/reverse-string/Cargo.toml @@ -0,0 +1,10 @@ +[dependencies] +unicode-segmentation = "1.10.1" + +[features] +grapheme = [] + +[package] +edition = "2021" +name = "reverse_string" +version = "1.2.0" diff --git a/rust/reverse-string/HELP.md b/rust/reverse-string/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/reverse-string/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/reverse-string/README.md b/rust/reverse-string/README.md new file mode 100644 index 00000000..7d2852f9 --- /dev/null +++ b/rust/reverse-string/README.md @@ -0,0 +1,55 @@ +# Reverse String + +Welcome to Reverse String on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Reverse a string + +For example: +input: "cool" +output: "looc" + +Test your function on this string: `uüu` and see what happens. Try to write a function that properly +reverses this string. Hint: grapheme clusters + +To get the bonus test to run, remove the ignore flag (`#[ignore]`) from the +last test, and execute the tests with: + +```bash +$ cargo test --features grapheme +``` + +You will need to use external libraries (a `crate` in rust lingo) for the bonus task. A good place to look for those is [crates.io](https://crates.io/), the official repository of crates. + +[Check the documentation](https://doc.rust-lang.org/cargo/guide/dependencies.html) for instructions on how to use external crates in your projects. + +## Source + +### Created by + +- @coriolinus + +### Contributed to by + +- @cbzehner +- @ccouzens +- @cwhakes +- @efx +- @ErikSchierboom +- @hunger +- @lutostag +- @ocstl +- @PaulT89 +- @petertseng +- @rofrol +- @rrredface +- @stringparser +- @TheDarkula +- @xakon +- @ZapAnton + +### Based on + +Introductory challenge to reverse an input string - https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb \ No newline at end of file diff --git a/rust/reverse-string/src/lib.rs b/rust/reverse-string/src/lib.rs new file mode 100644 index 00000000..f6b6c6d5 --- /dev/null +++ b/rust/reverse-string/src/lib.rs @@ -0,0 +1,10 @@ +use unicode_segmentation::UnicodeSegmentation; + +pub fn reverse(input: &str) -> String { + let graphemes = input.graphemes(true); + let mut result = String::new(); + for g in graphemes { + result.insert_str(0, g); + } + result +} diff --git a/rust/reverse-string/tests/reverse-string.rs b/rust/reverse-string/tests/reverse-string.rs new file mode 100644 index 00000000..c6f4ad7e --- /dev/null +++ b/rust/reverse-string/tests/reverse-string.rs @@ -0,0 +1,62 @@ +//! Tests for reverse-string +//! +//! Generated by [script][script] using [canonical data][canonical-data] +//! +//! [script]: https://github.com/exercism/rust/blob/b829ce2/bin/init_exercise.py +//! [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/reverse-string/canonical_data.json + +use reverse_string::*; + +/// Process a single test case for the property `reverse` +fn process_reverse_case(input: &str, expected: &str) { + assert_eq!(&reverse(input), expected) +} + +#[test] +/// empty string +fn test_an_empty_string() { + process_reverse_case("", ""); +} + +#[test] +/// a word +fn test_a_word() { + process_reverse_case("robot", "tobor"); +} + +#[test] +/// a capitalized word +fn test_a_capitalized_word() { + process_reverse_case("Ramen", "nemaR"); +} + +#[test] +/// a sentence with punctuation +fn test_a_sentence_with_punctuation() { + process_reverse_case("I'm hungry!", "!yrgnuh m'I"); +} + +#[test] +/// a palindrome +fn test_a_palindrome() { + process_reverse_case("racecar", "racecar"); +} + +#[test] +/// an even-sized word +fn test_an_even_sized_word() { + process_reverse_case("drawer", "reward"); +} + +#[test] +/// wide characters +fn test_wide_characters() { + process_reverse_case("子猫", "猫子"); +} + +#[test] +#[cfg(feature = "grapheme")] +/// grapheme clusters +fn test_grapheme_clusters() { + process_reverse_case("uüu", "uüu"); +} diff --git a/rust/robot-simulator/.gitignore b/rust/robot-simulator/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/robot-simulator/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/robot-simulator/Cargo.toml b/rust/robot-simulator/Cargo.toml new file mode 100644 index 00000000..fafcf46d --- /dev/null +++ b/rust/robot-simulator/Cargo.toml @@ -0,0 +1,7 @@ +[package] +edition = "2021" +name = "robot-simulator" +version = "2.2.0" + +[dependencies] +num = "0.4.0" \ No newline at end of file diff --git a/rust/robot-simulator/HELP.md b/rust/robot-simulator/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/robot-simulator/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/robot-simulator/README.md b/rust/robot-simulator/README.md new file mode 100644 index 00000000..4dcf1f9f --- /dev/null +++ b/rust/robot-simulator/README.md @@ -0,0 +1,59 @@ +# Robot Simulator + +Welcome to Robot Simulator on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Write a robot simulator. + +A robot factory's test facility needs a program to verify robot movements. + +The robots have three possible movements: + +- turn right +- turn left +- advance + +Robots are placed on a hypothetical infinite grid, facing a particular +direction (north, east, south, or west) at a set of {x,y} coordinates, +e.g., {3,8}, with coordinates increasing to the north and east. + +The robot then receives a number of instructions, at which point the +testing facility verifies the robot's new position, and in which +direction it is pointing. + +- The letter-string "RAALAL" means: + - Turn right + - Advance twice + - Turn left + - Advance once + - Turn left yet again +- Say a robot starts at {7, 3} facing north. Then running this stream + of instructions should leave it at {9, 4} facing west. + +## Source + +### Created by + +- @IanWhitney + +### Contributed to by + +- @ashleygwilliams +- @coriolinus +- @cwhakes +- @efx +- @ErikSchierboom +- @IanWhitney +- @lutostag +- @nfiles +- @petertseng +- @rofrol +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +Inspired by an interview question at a famous company. \ No newline at end of file diff --git a/rust/robot-simulator/src/lib.rs b/rust/robot-simulator/src/lib.rs new file mode 100644 index 00000000..20a55136 --- /dev/null +++ b/rust/robot-simulator/src/lib.rs @@ -0,0 +1,83 @@ +// The code below is a stub. Just enough to satisfy the compiler. +// In order to pass the tests you can add-to or change any of this code. +use num::{complex::Complex, One}; + +#[derive(PartialEq, Eq, Debug)] +pub enum Direction { + North, + East, + South, + West, +} + +impl Direction { + fn from_complex(z: Complex) -> &'static Direction { + match z { + z if z == Complex::i() => &Self::North, + z if z == Complex::one() => &Self::East, + z if z == -Complex::i() => &Self::South, + z if z == -Complex::one() => &Self::West, + _ => panic!(""), + } + } +} + +pub struct Robot { + position: Complex, + direction: Complex, +} + +impl Robot { + pub fn new(x: i32, y: i32, d: Direction) -> Self { + Robot { + position: Complex::new(x, y), + direction: Complex::i().powi(1 - (d as i32)), + } + } + + #[must_use] + pub fn turn_right(self) -> Self { + Robot { + direction: self.direction * -Complex::i(), + ..self + } + } + + #[must_use] + pub fn turn_left(self) -> Self { + Robot { + direction: self.direction * Complex::i(), + ..self + } + } + + #[must_use] + pub fn advance(self) -> Self { + Robot { + position: self.position + self.direction, + ..self + } + } + + #[must_use] + pub fn instructions(self, instructions: &str) -> Self { + let mut n = self; + for c in instructions.chars() { + n = match c { + 'L' => n.turn_left(), + 'R' => n.turn_right(), + 'A' => n.advance(), + _ => panic!("Unknown instruction: {c}"), + }; + } + n + } + + pub fn position(&self) -> (i32, i32) { + (self.position.re, self.position.im) + } + + pub fn direction(&self) -> &Direction { + &Direction::from_complex(self.direction) + } +} diff --git a/rust/robot-simulator/tests/robot-simulator.rs b/rust/robot-simulator/tests/robot-simulator.rs new file mode 100644 index 00000000..14235e90 --- /dev/null +++ b/rust/robot-simulator/tests/robot-simulator.rs @@ -0,0 +1,126 @@ +use robot_simulator::*; + +#[test] +fn robots_are_created_with_position_and_direction() { + let robot = Robot::new(0, 0, Direction::North); + assert_eq!((0, 0), robot.position()); + assert_eq!(&Direction::North, robot.direction()); +} + +#[test] +fn positions_can_be_negative() { + let robot = Robot::new(-1, -1, Direction::South); + assert_eq!((-1, -1), robot.position()); + assert_eq!(&Direction::South, robot.direction()); +} + +#[test] +fn turning_right_does_not_change_position() { + let robot = Robot::new(0, 0, Direction::North).turn_right(); + assert_eq!((0, 0), robot.position()); +} + +#[test] +fn turning_right_from_north_points_the_robot_east() { + let robot = Robot::new(0, 0, Direction::North).turn_right(); + assert_eq!(&Direction::East, robot.direction()); +} + +#[test] +fn turning_right_from_east_points_the_robot_south() { + let robot = Robot::new(0, 0, Direction::East).turn_right(); + assert_eq!(&Direction::South, robot.direction()); +} + +#[test] +fn turning_right_from_south_points_the_robot_west() { + let robot = Robot::new(0, 0, Direction::South).turn_right(); + assert_eq!(&Direction::West, robot.direction()); +} + +#[test] +fn turning_right_from_west_points_the_robot_north() { + let robot = Robot::new(0, 0, Direction::West).turn_right(); + assert_eq!(&Direction::North, robot.direction()); +} + +#[test] +fn turning_left_does_not_change_position() { + let robot = Robot::new(0, 0, Direction::North).turn_left(); + assert_eq!((0, 0), robot.position()); +} + +#[test] +fn turning_left_from_north_points_the_robot_west() { + let robot = Robot::new(0, 0, Direction::North).turn_left(); + assert_eq!(&Direction::West, robot.direction()); +} + +#[test] +fn turning_left_from_west_points_the_robot_south() { + let robot = Robot::new(0, 0, Direction::West).turn_left(); + assert_eq!(&Direction::South, robot.direction()); +} + +#[test] +fn turning_left_from_south_points_the_robot_east() { + let robot = Robot::new(0, 0, Direction::South).turn_left(); + assert_eq!(&Direction::East, robot.direction()); +} + +#[test] +fn turning_left_from_east_points_the_robot_north() { + let robot = Robot::new(0, 0, Direction::East).turn_left(); + assert_eq!(&Direction::North, robot.direction()); +} + +#[test] +fn advance_does_not_change_the_direction() { + let robot = Robot::new(0, 0, Direction::North).advance(); + assert_eq!(&Direction::North, robot.direction()); +} + +#[test] +fn advance_increases_the_y_coordinate_by_one_when_facing_north() { + let robot = Robot::new(0, 0, Direction::North).advance(); + assert_eq!((0, 1), robot.position()); +} + +#[test] +fn advance_decreases_the_y_coordinate_by_one_when_facing_south() { + let robot = Robot::new(0, 0, Direction::South).advance(); + assert_eq!((0, -1), robot.position()); +} + +#[test] +fn advance_increases_the_x_coordinate_by_one_when_facing_east() { + let robot = Robot::new(0, 0, Direction::East).advance(); + assert_eq!((1, 0), robot.position()); +} + +#[test] +fn advance_decreases_the_x_coordinate_by_one_when_facing_west() { + let robot = Robot::new(0, 0, Direction::West).advance(); + assert_eq!((-1, 0), robot.position()); +} + +#[test] +fn follow_instructions_to_move_west_and_north() { + let robot = Robot::new(0, 0, Direction::North).instructions("LAAARALA"); + assert_eq!((-4, 1), robot.position()); + assert_eq!(&Direction::West, robot.direction()); +} + +#[test] +fn follow_instructions_to_move_west_and_south() { + let robot = Robot::new(2, -7, Direction::East).instructions("RRAAAAALA"); + assert_eq!((-3, -8), robot.position()); + assert_eq!(&Direction::South, robot.direction()); +} + +#[test] +fn follow_instructions_to_move_east_and_north() { + let robot = Robot::new(8, 4, Direction::South).instructions("LAAARRRALLLL"); + assert_eq!((11, 5), robot.position()); + assert_eq!(&Direction::North, robot.direction()); +} diff --git a/rust/semi-structured-logs/.gitignore b/rust/semi-structured-logs/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/semi-structured-logs/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/semi-structured-logs/Cargo.toml b/rust/semi-structured-logs/Cargo.toml new file mode 100644 index 00000000..166e966a --- /dev/null +++ b/rust/semi-structured-logs/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "semi_structured_logs" +version = "0.1.0" +edition = "2021" + +[features] +add-a-variant = [] + +[dependencies] diff --git a/rust/semi-structured-logs/HELP.md b/rust/semi-structured-logs/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/semi-structured-logs/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/semi-structured-logs/HINTS.md b/rust/semi-structured-logs/HINTS.md new file mode 100644 index 00000000..1804e145 --- /dev/null +++ b/rust/semi-structured-logs/HINTS.md @@ -0,0 +1,13 @@ +# Hints + +## General + +- [Rust By Example - Enums][rbe-enums] +- [cheats.rs - Basic Types][cheats-types] + +## 1. Emit semi-structured messages + +- `match` comes in handy when working with enums. In this case, see how you might use it when figuring how what kind of log message to generate. + +[rbe-enums]: https://doc.rust-lang.org/stable/rust-by-example/custom_types/enum.html#enums +[cheats-types]: https://cheats.rs/#basic-types \ No newline at end of file diff --git a/rust/semi-structured-logs/README.md b/rust/semi-structured-logs/README.md new file mode 100644 index 00000000..955299c7 --- /dev/null +++ b/rust/semi-structured-logs/README.md @@ -0,0 +1,64 @@ +# Semi Structured Logs + +Welcome to Semi Structured Logs on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +Enums, short for enumerations, are a type that limits all possible values of some data. The possible values of an `enum` are called variants. Enums also work well with `match` and other control flow operators to help you express intent in your Rust programs. + +## Instructions + +In this exercise you'll be generating semi-structured log messages. + +## 1. Emit semi-structured messages + +You'll start with some stubbed functions and the following enum: + +```rust +#[derive(Clone, PartialEq, Debug)] +pub enum LogLevel { + Info, + Warning, + Error, +} +``` + +Your goal is to emit a log message as follows: `"[]: "`. +You'll need to implement functions that correspond with log levels. + +For example, the below snippet demonstrates an expected output for the `log` function. + +```rust +log(LogLevel::Error, "Stack overflow") +// Returns: "[ERROR]: Stack overflow" +``` + +And for `info`: + +```rust +info("Timezone changed") +// Returns: "[INFO]: Timezone changed" +``` + +Have fun! + +## 2. Optional further practice + +There is a feature-gated test in this suite. +Feature gates disable compilation entirely for certain sections of your program. +They will be covered later. +For now just know that there is a test which is only built and run when you use a special testing invocation: + +```sh +cargo test --features add-a-variant +``` + +This test is meant to show you how to add a variant to your enum. + +## Source + +### Created by + +- @efx \ No newline at end of file diff --git a/rust/semi-structured-logs/src/lib.rs b/rust/semi-structured-logs/src/lib.rs new file mode 100644 index 00000000..f4bc39f9 --- /dev/null +++ b/rust/semi-structured-logs/src/lib.rs @@ -0,0 +1,35 @@ +// This stub file contains items that aren't used yet; feel free to remove this module attribute +// to enable stricter warnings. +#![allow(unused)] + +use std::fmt; + +/// various log levels +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum LogLevel { + Info, + Warning, + Error, +} + +// Custom formatter behavior +impl fmt::Display for LogLevel { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let self_str = format!("{:?}", self); + write!(f, "{}", self_str.to_uppercase()) + } +} + +/// primary function for emitting logs +pub fn log(level: LogLevel, message: &str) -> String { + return format!("[{level}]: {message}"); +} +pub fn info(message: &str) -> String { + log(LogLevel::Info, message) +} +pub fn warn(message: &str) -> String { + log(LogLevel::Warning, message) +} +pub fn error(message: &str) -> String { + log(LogLevel::Error, message) +} diff --git a/rust/semi-structured-logs/tests/semi-structured-logs.rs b/rust/semi-structured-logs/tests/semi-structured-logs.rs new file mode 100644 index 00000000..fc45208c --- /dev/null +++ b/rust/semi-structured-logs/tests/semi-structured-logs.rs @@ -0,0 +1,47 @@ +use semi_structured_logs::{error, info, log, warn, LogLevel}; + +#[test] +fn emits_info() { + assert_eq!(info("Timezone changed"), "[INFO]: Timezone changed"); +} + +#[test] +fn emits_warning() { + assert_eq!(warn("Timezone not set"), "[WARNING]: Timezone not set"); +} + +#[test] +fn emits_error() { + assert_eq!(error("Disk full"), "[ERROR]: Disk full"); +} + +#[test] +fn log_emits_info() { + assert_eq!( + log(LogLevel::Info, "Timezone changed"), + "[INFO]: Timezone changed" + ); +} + +#[test] +fn log_emits_warning() { + assert_eq!( + log(LogLevel::Warning, "Timezone not set"), + "[WARNING]: Timezone not set" + ); +} + +#[test] +fn log_emits_error() { + assert_eq!(log(LogLevel::Error, "Disk full"), "[ERROR]: Disk full"); +} + +#[test] +#[cfg(feature = "add-a-variant")] +fn add_a_variant() { + // this test won't even compile until the enum is complete, which is why it is feature-gated. + assert_eq!( + log(LogLevel::Debug, "reached line 123"), + "[DEBUG]: reached line 123", + ); +} diff --git a/rust/space-age/.gitignore b/rust/space-age/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/space-age/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/space-age/Cargo.toml b/rust/space-age/Cargo.toml new file mode 100644 index 00000000..9640244b --- /dev/null +++ b/rust/space-age/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "space-age" +version = "1.2.0" diff --git a/rust/space-age/HELP.md b/rust/space-age/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/space-age/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/space-age/README.md b/rust/space-age/README.md new file mode 100644 index 00000000..97247845 --- /dev/null +++ b/rust/space-age/README.md @@ -0,0 +1,69 @@ +# Space Age + +Welcome to Space Age on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Given an age in seconds, calculate how old someone would be on: + + - Mercury: orbital period 0.2408467 Earth years + - Venus: orbital period 0.61519726 Earth years + - Earth: orbital period 1.0 Earth years, 365.25 Earth days, or 31557600 seconds + - Mars: orbital period 1.8808158 Earth years + - Jupiter: orbital period 11.862615 Earth years + - Saturn: orbital period 29.447498 Earth years + - Uranus: orbital period 84.016846 Earth years + - Neptune: orbital period 164.79132 Earth years + +So if you were told someone were 1,000,000,000 seconds old, you should +be able to say that they're 31.69 Earth-years old. + +If you're wondering why Pluto didn't make the cut, go watch [this +youtube video](http://www.youtube.com/watch?v=Z_2gbGXzFbs). + +Some Rust topics you may want to read about while solving this problem: + +- Traits, both the From trait and implementing your own traits +- Default method implementations for traits +- Macros, the use of a macro could reduce boilerplate and increase readability + for this exercise. For instance, + [a macro can implement a trait for multiple types at once](https://stackoverflow.com/questions/39150216/implementing-a-trait-for-multiple-types-at-once), + though it is fine to implement `years_during` in the Planet trait itself. A macro could + define both the structs and their implementations. Info to get started with macros can + be found at: + + - [The Macros chapter in The Rust Programming Language](https://doc.rust-lang.org/stable/book/ch19-06-macros.html) + - [an older version of the Macros chapter with helpful detail](https://doc.rust-lang.org/1.30.0/book/first-edition/macros.html) + - [Rust By Example](https://doc.rust-lang.org/stable/rust-by-example/macros.html) + +## Source + +### Created by + +- @IanWhitney + +### Contributed to by + +- @ashleygwilliams +- @bobahop +- @coriolinus +- @cwhakes +- @durka +- @eddyp +- @efx +- @ErikSchierboom +- @IanWhitney +- @joshgoebel +- @lutostag +- @nfiles +- @ocstl +- @petertseng +- @rofrol +- @stringparser +- @xakon +- @ZapAnton + +### Based on + +Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial. - http://pine.fm/LearnToProgram/?Chapter=01 \ No newline at end of file diff --git a/rust/space-age/src/lib.rs b/rust/space-age/src/lib.rs new file mode 100644 index 00000000..0d7a633f --- /dev/null +++ b/rust/space-age/src/lib.rs @@ -0,0 +1,48 @@ +// The code below is a stub. Just enough to satisfy the compiler. +// In order to pass the tests you can add-to or change any of this code. + +#[derive(Debug)] +pub struct Duration { + earth_years: f64, +} + +impl From for Duration { + fn from(s: u64) -> Self { + Self { + earth_years: s as f64 / 31557600.0, + } + } +} + +pub trait Planet { + fn years_during(d: &Duration) -> f64 { + unimplemented!( + "convert a duration ({d:?}) to the number of years on this planet for that duration" + ); + } +} + +macro_rules! generate_planets { + ($($t:ident => $e:expr),+) => { + $( + pub struct $t; + + impl Planet for $t { + fn years_during(d: &Duration) -> f64 { + d.earth_years / $e + } + } + )* + }; +} + +generate_planets!( + Mercury => 0.2408467, + Venus => 0.61519726, + Earth => 1.0, + Mars => 1.8808158, + Jupiter => 11.862615, + Saturn => 29.447498, + Uranus => 84.016846, + Neptune => 164.79132 +); diff --git a/rust/space-age/tests/space-age.rs b/rust/space-age/tests/space-age.rs new file mode 100644 index 00000000..90f4c20f --- /dev/null +++ b/rust/space-age/tests/space-age.rs @@ -0,0 +1,57 @@ +use space_age::*; + +fn assert_in_delta(expected: f64, actual: f64) { + let diff: f64 = (expected - actual).abs(); + let delta: f64 = 0.01; + if diff > delta { + panic!("Your result of {actual} should be within {delta} of the expected result {expected}") + } +} + +#[test] +fn earth_age() { + let duration = Duration::from(1_000_000_000); + assert_in_delta(31.69, Earth::years_during(&duration)); +} + +#[test] +fn mercury_age() { + let duration = Duration::from(2_134_835_688); + assert_in_delta(280.88, Mercury::years_during(&duration)); +} + +#[test] +fn venus_age() { + let duration = Duration::from(189_839_836); + assert_in_delta(9.78, Venus::years_during(&duration)); +} + +#[test] +fn mars_age() { + let duration = Duration::from(2_129_871_239); + assert_in_delta(35.88, Mars::years_during(&duration)); +} + +#[test] +fn jupiter_age() { + let duration = Duration::from(901_876_382); + assert_in_delta(2.41, Jupiter::years_during(&duration)); +} + +#[test] +fn saturn_age() { + let duration = Duration::from(2_000_000_000); + assert_in_delta(2.15, Saturn::years_during(&duration)); +} + +#[test] +fn uranus_age() { + let duration = Duration::from(1_210_123_456); + assert_in_delta(0.46, Uranus::years_during(&duration)); +} + +#[test] +fn neptune_age() { + let duration = Duration::from(1_821_023_456); + assert_in_delta(0.35, Neptune::years_during(&duration)); +} diff --git a/rust/sublist/.gitignore b/rust/sublist/.gitignore new file mode 100644 index 00000000..db7f315c --- /dev/null +++ b/rust/sublist/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/rust/sublist/Cargo.toml b/rust/sublist/Cargo.toml new file mode 100644 index 00000000..6493981c --- /dev/null +++ b/rust/sublist/Cargo.toml @@ -0,0 +1,4 @@ +[package] +edition = "2021" +name = "sublist" +version = "0.0.0" diff --git a/rust/sublist/HELP.md b/rust/sublist/HELP.md new file mode 100644 index 00000000..7857ff6a --- /dev/null +++ b/rust/sublist/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs Cargo.toml` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][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! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.org/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.org/docs/community/contributors \ No newline at end of file diff --git a/rust/sublist/README.md b/rust/sublist/README.md new file mode 100644 index 00000000..2c905a43 --- /dev/null +++ b/rust/sublist/README.md @@ -0,0 +1,49 @@ +# Sublist + +Welcome to Sublist on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Given two lists determine if the first list is contained within the second +list, if the second list is contained within the first list, if both lists are +contained within each other or if none of these are true. + +Specifically, a list A is a sublist of list B if by dropping 0 or more elements +from the front of B and 0 or more elements from the back of B you get a list +that's completely equal to A. + +Examples: + + * A = [1, 2, 3], B = [1, 2, 3, 4, 5], A is a sublist of B + * A = [3, 4, 5], B = [1, 2, 3, 4, 5], A is a sublist of B + * A = [3, 4], B = [1, 2, 3, 4, 5], A is a sublist of B + * A = [1, 2, 3], B = [1, 2, 3], A is equal to B + * A = [1, 2, 3, 4, 5], B = [2, 3, 4], A is a superlist of B + * A = [1, 2, 4], B = [1, 2, 3, 4, 5], A is not a superlist of, sublist of or equal to B + +## Source + +### Created by + +- @EduardoBautista + +### Contributed to by + +- @ashleygwilliams +- @coriolinus +- @cwhakes +- @eddyp +- @EduardoBautista +- @efx +- @ErikSchierboom +- @IanWhitney +- @kytrinyx +- @lutostag +- @mkantor +- @nfiles +- @petertseng +- @rofrol +- @stringparser +- @xakon +- @ZapAnton \ No newline at end of file diff --git a/rust/sublist/src/lib.rs b/rust/sublist/src/lib.rs new file mode 100644 index 00000000..6c68672e --- /dev/null +++ b/rust/sublist/src/lib.rs @@ -0,0 +1,13 @@ +#[derive(Debug, PartialEq, Eq)] +#[allow(dead_code)] +pub enum Comparison { + Equal, + Sublist, + Superlist, + Unequal, +} + +pub fn sublist(_first_list: &[T], _second_list: &[T]) -> Comparison { + unimplemented!("Determine if the first list is equal to, sublist of, superlist of or unequal to the second list."); +} + diff --git a/rust/sublist/src/main.rs b/rust/sublist/src/main.rs new file mode 100644 index 00000000..0716a0dc --- /dev/null +++ b/rust/sublist/src/main.rs @@ -0,0 +1,12 @@ +use sublist::Comparison; + + + +fn main() { + // Comparison::Equal; + let arr = &[1,2,3,4,5]; + let subarr = &arr[1..3]; + dbg!("Array: {}", &arr); + dbg!("Subarray: {}", &subarr); + println!("Well, hello!"); +} \ No newline at end of file diff --git a/rust/sublist/tests/sublist.rs b/rust/sublist/tests/sublist.rs new file mode 100644 index 00000000..9653ab15 --- /dev/null +++ b/rust/sublist/tests/sublist.rs @@ -0,0 +1,127 @@ +use sublist::{sublist, Comparison}; + +#[test] +fn empty_equals_empty() { + let v: &[u32] = &[]; + + assert_eq!(Comparison::Equal, sublist(v, v)); +} + +#[test] +#[ignore] +fn test_empty_is_a_sublist_of_anything() { + assert_eq!(Comparison::Sublist, sublist(&[], &['a', 's', 'd', 'f'])); +} + +#[test] +#[ignore] +fn test_anything_is_a_superlist_of_empty() { + assert_eq!(Comparison::Superlist, sublist(&['a', 's', 'd', 'f'], &[])); +} + +#[test] +#[ignore] +fn test_1_is_not_2() { + assert_eq!(Comparison::Unequal, sublist(&[1], &[2])); +} + +#[test] +#[ignore] +fn test_compare_larger_equal_lists() { + use std::iter::repeat; + + let v: Vec = repeat('x').take(1000).collect(); + + assert_eq!(Comparison::Equal, sublist(&v, &v)); +} + +#[test] +#[ignore] +fn test_sublist_at_start() { + assert_eq!(Comparison::Sublist, sublist(&[1, 2, 3], &[1, 2, 3, 4, 5])); +} + +#[test] +#[ignore] +fn sublist_in_middle() { + assert_eq!(Comparison::Sublist, sublist(&[4, 3, 2], &[5, 4, 3, 2, 1])); +} + +#[test] +#[ignore] +fn sublist_at_end() { + assert_eq!(Comparison::Sublist, sublist(&[3, 4, 5], &[1, 2, 3, 4, 5])); +} + +#[test] +#[ignore] +fn partially_matching_sublist_at_start() { + assert_eq!(Comparison::Sublist, sublist(&[1, 1, 2], &[1, 1, 1, 2])); +} + +#[test] +#[ignore] +fn sublist_early_in_huge_list() { + let huge: Vec = (1..1_000_000).collect(); + + assert_eq!(Comparison::Sublist, sublist(&[3, 4, 5], &huge)); +} + +#[test] +#[ignore] +fn huge_sublist_not_in_huge_list() { + let v1: Vec = (10..1_000_001).collect(); + let v2: Vec = (1..1_000_000).collect(); + + assert_eq!(Comparison::Unequal, sublist(&v1, &v2)); +} + +#[test] +#[ignore] +fn superlist_at_start() { + assert_eq!(Comparison::Superlist, sublist(&[1, 2, 3, 4, 5], &[1, 2, 3])); +} + +#[test] +#[ignore] +fn superlist_in_middle() { + assert_eq!(Comparison::Superlist, sublist(&[5, 4, 3, 2, 1], &[4, 3, 2])); +} + +#[test] +#[ignore] +fn superlist_at_end() { + assert_eq!(Comparison::Superlist, sublist(&[1, 2, 3, 4, 5], &[3, 4, 5])); +} + +#[test] +#[ignore] +fn second_list_missing_element_from_first_list() { + assert_eq!(Comparison::Unequal, sublist(&[1, 2, 3], &[1, 3])); +} + +#[test] +#[ignore] +fn superlist_early_in_huge_list() { + let huge: Vec = (1..1_000_000).collect(); + + assert_eq!(Comparison::Superlist, sublist(&huge, &[3, 4, 5])); +} + +#[test] +#[ignore] +fn recurring_values_sublist() { + assert_eq!( + Comparison::Sublist, + sublist(&[1, 2, 1, 2, 3], &[1, 2, 3, 1, 2, 1, 2, 3, 2, 1]) + ); +} + +#[test] +#[ignore] +fn recurring_values_unequal() { + assert_eq!( + Comparison::Unequal, + sublist(&[1, 2, 1, 2, 3], &[1, 2, 3, 1, 2, 3, 2, 3, 2, 1]) + ); +}