aboutsummaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
Diffstat (limited to 'rust')
-rw-r--r--rust/triangle/.exercism/metadata.json1
-rw-r--r--rust/triangle/.gitignore8
-rw-r--r--rust/triangle/Cargo.toml7
-rw-r--r--rust/triangle/README.md131
-rw-r--r--rust/triangle/src/lib.rs27
-rw-r--r--rust/triangle/tests/triangle.rs183
6 files changed, 357 insertions, 0 deletions
diff --git a/rust/triangle/.exercism/metadata.json b/rust/triangle/.exercism/metadata.json
new file mode 100644
index 0000000..3210561
--- /dev/null
+++ b/rust/triangle/.exercism/metadata.json
@@ -0,0 +1 @@
{"track":"rust","exercise":"triangle","id":"c3fbb35fa9084358852226269ba3ed1b","url":"https://exercism.io/my/solutions/c3fbb35fa9084358852226269ba3ed1b","handle":"dyamon","is_requester":true,"auto_approve":false} \ No newline at end of file
diff --git a/rust/triangle/.gitignore b/rust/triangle/.gitignore
new file mode 100644
index 0000000..db7f315
--- /dev/null
+++ b/rust/triangle/.gitignore
@@ -0,0 +1,8 @@
1# Generated by Cargo
2# will have compiled files and executables
3/target/
4**/*.rs.bk
5
6# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
8Cargo.lock
diff --git a/rust/triangle/Cargo.toml b/rust/triangle/Cargo.toml
new file mode 100644
index 0000000..4042a35
--- /dev/null
+++ b/rust/triangle/Cargo.toml
@@ -0,0 +1,7 @@
1[package]
2edition = "2018"
3name = "triangle"
4version = "0.0.0"
5
6[features]
7generic = []
diff --git a/rust/triangle/README.md b/rust/triangle/README.md
new file mode 100644
index 0000000..fe094f7
--- /dev/null
+++ b/rust/triangle/README.md
@@ -0,0 +1,131 @@
1# Triangle
2
3Determine if a triangle is equilateral, isosceles, or scalene.
4
5An _equilateral_ triangle has all three sides the same length.
6
7An _isosceles_ triangle has at least two sides the same length. (It is sometimes
8specified as having exactly two sides the same length, but for the purposes of
9this exercise we'll say at least two.)
10
11A _scalene_ triangle has all sides of different lengths.
12
13## Note
14
15For a shape to be a triangle at all, all sides have to be of length > 0, and
16the sum of the lengths of any two sides must be greater than or equal to the
17length of the third side. See [Triangle Inequality](https://en.wikipedia.org/wiki/Triangle_inequality).
18
19## Dig Deeper
20
21The case where the sum of the lengths of two sides _equals_ that of the
22third is known as a _degenerate_ triangle - it has zero area and looks like
23a single line. Feel free to add your own code/tests to check for degenerate triangles.
24
25# Triangle in Rust
26
27- [Result](https://doc.rust-lang.org/std/result/index.html)
28
29Implementation of this can take many forms. Here are some topics that may help you, depending on the approach you take.
30
31- [Enums](https://doc.rust-lang.org/book/2018-edition/ch06-00-enums.html)
32- [Traits](https://doc.rust-lang.org/book/2018-edition/ch10-02-traits.html)
33- [BTreeSet](https://doc.rust-lang.org/std/collections/btree_set/struct.BTreeSet.html)
34
35Or maybe you will come up with an approach that uses none of those!
36
37## Non-integer lengths
38
39The base exercise tests identification of triangles whose sides are all
40integers. However, some triangles cannot be represented by pure integers. A simple example is a right triangle (an isosceles triangle whose equal sides are separated by 90 degrees) whose equal sides both have length of 1. Its hypotenuse is the square root of 2, which is an irrational number: no simple multiplication can represent this number as an integer.
41
42It would be tedious to rewrite the analysis functions to handle both integer and floating-point cases, and particularly tedious to do so for all potential integer and floating point types: given signed and unsigned variants of bitwidths 8, 16, 32, 64, and 128, that would be 10 reimplementations of fundamentally the same code even before considering floats!
43
44There's a better way: [generics](https://doc.rust-lang.org/stable/book/2018-edition/ch10-00-generics.html). By rewriting your Triangle as a `Triangle<T>`, you can write your code once, and hand off the work of generating all those specializations to the compiler. Note that in order to use mathematical operations, you'll need to constrain your generic type to types which support those operations using traits.
45
46There are some bonus tests you can run which test your implementation on floating-point numbers. To enable them, run your tests with the `generic` feature flag, like this:
47
48```bash
49cargo test --features generic
50```
51
52
53## Rust Installation
54
55Refer to the [exercism help page][help-page] for Rust installation and learning
56resources.
57
58## Writing the Code
59
60Execute the tests with:
61
62```bash
63$ cargo test
64```
65
66All but the first test have been ignored. After you get the first test to
67pass, open the tests source file which is located in the `tests` directory
68and remove the `#[ignore]` flag from the next test and get the tests to pass
69again. Each separate test is a function with `#[test]` flag above it.
70Continue, until you pass every test.
71
72If you wish to run all ignored tests without editing the tests source file, use:
73
74```bash
75$ cargo test -- --ignored
76```
77
78To run a specific test, for example `some_test`, you can use:
79
80```bash
81$ cargo test some_test
82```
83
84If the specific test is ignored use:
85
86```bash
87$ cargo test some_test -- --ignored
88```
89
90To learn more about Rust tests refer to the [online test documentation][rust-tests]
91
92Make sure to read the [Modules][modules] chapter if you
93haven't already, it will help you with organizing your files.
94
95## Further improvements
96
97After you have solved the exercise, please consider using the additional utilities, described in the [installation guide](https://exercism.io/tracks/rust/installation), to further refine your final solution.
98
99To format your solution, inside the solution directory use
100
101```bash
102cargo fmt
103```
104
105To see, if your solution contains some common ineffective use cases, inside the solution directory use
106
107```bash
108cargo clippy --all-targets
109```
110
111## Submitting the solution
112
113Generally 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.
114
115## Feedback, Issues, Pull Requests
116
117The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help!
118
119If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md).
120
121[help-page]: https://exercism.io/tracks/rust/learning
122[modules]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
123[cargo]: https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html
124[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html
125
126## Source
127
128The Ruby Koans triangle project, parts 1 & 2 [http://rubykoans.com](http://rubykoans.com)
129
130## Submitting Incomplete Solutions
131It's possible to submit an incomplete solution so you can see how others have completed the exercise.
diff --git a/rust/triangle/src/lib.rs b/rust/triangle/src/lib.rs
new file mode 100644
index 0000000..f4486fe
--- /dev/null
+++ b/rust/triangle/src/lib.rs
@@ -0,0 +1,27 @@
1use std::ops::Add;
2
3pub struct Triangle<T>([T; 3]);
4
5impl<T> Triangle<T>
6where
7 T: Add<Output = T> + PartialOrd + Copy,
8{
9 pub fn build(sides: [T; 3]) -> Option<Triangle<T>> {
10 Some(Triangle(sides))
11 .filter(|_| sides[0] + sides[1] > sides[2])
12 .filter(|_| sides[1] + sides[2] > sides[0])
13 .filter(|_| sides[0] + sides[2] > sides[1])
14 }
15
16 pub fn is_equilateral(&self) -> bool {
17 self.0[0] == self.0[1] && self.0[1] == self.0[2]
18 }
19
20 pub fn is_scalene(&self) -> bool {
21 self.0[0] != self.0[1] && self.0[1] != self.0[2] && self.0[0] != self.0[2]
22 }
23
24 pub fn is_isosceles(&self) -> bool {
25 !(self.is_equilateral() || self.is_scalene())
26 }
27}
diff --git a/rust/triangle/tests/triangle.rs b/rust/triangle/tests/triangle.rs
new file mode 100644
index 0000000..f3e4d8a
--- /dev/null
+++ b/rust/triangle/tests/triangle.rs
@@ -0,0 +1,183 @@
1use triangle::*;
2
3#[test]
4fn positive_length_sides_are_ok() {
5 let sides = [2, 2, 2];
6 let triangle = Triangle::build(sides);
7 assert!(triangle.is_some());
8}
9
10#[test]
11fn zero_length_sides_are_illegal() {
12 let sides = [0, 0, 0];
13 let triangle = Triangle::build(sides);
14 assert!(triangle.is_none());
15}
16
17#[test]
18fn one_length_zero_side_first() {
19 let sides = [0, 2, 2];
20 let triangle = Triangle::build(sides);
21 assert!(triangle.is_none());
22}
23
24#[test]
25fn one_length_zero_side_second() {
26 let sides = [2, 0, 2];
27 let triangle = Triangle::build(sides);
28 assert!(triangle.is_none());
29}
30
31#[test]
32fn one_length_zero_side_third() {
33 let sides = [2, 2, 0];
34 let triangle = Triangle::build(sides);
35 assert!(triangle.is_none());
36}
37
38#[test]
39fn equilateral_triangles_have_equal_sides() {
40 let sides = [2, 2, 2];
41 let triangle = Triangle::build(sides).unwrap();
42 assert!(triangle.is_equilateral());
43 assert!(!triangle.is_scalene());
44}
45
46#[test]
47fn larger_equilateral_triangles_have_equal_sides() {
48 let sides = [10, 10, 10];
49 let triangle = Triangle::build(sides).unwrap();
50 assert!(triangle.is_equilateral());
51 assert!(!triangle.is_scalene());
52}
53
54#[test]
55fn isosceles_triangles_have_two_equal_sides_one() {
56 let sides = [3, 4, 4];
57 let triangle = Triangle::build(sides).unwrap();
58 assert!(!triangle.is_equilateral());
59 assert!(triangle.is_isosceles());
60 assert!(!triangle.is_scalene());
61}
62
63#[test]
64fn isosceles_triangles_have_two_equal_sides_two() {
65 let sides = [4, 4, 3];
66 let triangle = Triangle::build(sides).unwrap();
67 assert!(!triangle.is_equilateral());
68 assert!(triangle.is_isosceles());
69 assert!(!triangle.is_scalene());
70}
71
72#[test]
73fn isosceles_triangles_have_two_equal_sides_three() {
74 let sides = [4, 3, 4];
75 let triangle = Triangle::build(sides).unwrap();
76 assert!(!triangle.is_equilateral());
77 assert!(triangle.is_isosceles());
78 assert!(!triangle.is_scalene());
79}
80
81#[test]
82fn isosceles_triangles_have_two_equal_sides_four() {
83 let sides = [4, 7, 4];
84 let triangle = Triangle::build(sides).unwrap();
85 assert!(!triangle.is_equilateral());
86 assert!(triangle.is_isosceles());
87 assert!(!triangle.is_scalene());
88}
89
90#[test]
91fn scalene_triangle_has_no_equal_sides_one() {
92 let sides = [3, 4, 5];
93 let triangle = Triangle::build(sides).unwrap();
94 assert!(!triangle.is_equilateral());
95 assert!(!triangle.is_isosceles());
96 assert!(triangle.is_scalene());
97}
98
99#[test]
100fn scalene_triangle_has_no_equal_sides_two() {
101 let sides = [5, 4, 6];
102 let triangle = Triangle::build(sides).unwrap();
103 assert!(!triangle.is_equilateral());
104 assert!(!triangle.is_isosceles());
105 assert!(triangle.is_scalene());
106}
107
108#[test]
109fn scalene_triangle_has_no_equal_sides_three() {
110 let sides = [10, 11, 12];
111 let triangle = Triangle::build(sides).unwrap();
112 assert!(!triangle.is_equilateral());
113 assert!(!triangle.is_isosceles());
114 assert!(triangle.is_scalene());
115}
116
117#[test]
118fn scalene_triangle_has_no_equal_sides_four() {
119 let sides = [5, 4, 2];
120 let triangle = Triangle::build(sides).unwrap();
121 assert!(!triangle.is_equilateral());
122 assert!(!triangle.is_isosceles());
123 assert!(triangle.is_scalene());
124}
125
126#[test]
127fn sum_of_two_sides_must_equal_or_exceed_the_remaining_side_one() {
128 let sides = [7, 3, 2];
129 let triangle = Triangle::build(sides);
130 assert!(triangle.is_none());
131}
132
133#[test]
134fn sum_of_two_sides_must_equal_or_exceed_the_remaining_side_two() {
135 let sides = [1, 1, 3];
136 let triangle = Triangle::build(sides);
137 assert!(triangle.is_none());
138}
139
140#[test]
141#[cfg(feature = "generic")]
142fn scalene_triangle_with_floating_point_sides() {
143 let sides = [0.4, 0.6, 0.3];
144 let triangle = Triangle::build(sides).unwrap();
145 assert!(!triangle.is_equilateral());
146 assert!(!triangle.is_isosceles());
147 assert!(triangle.is_scalene());
148}
149
150#[test]
151#[cfg(feature = "generic")]
152fn equilateral_triangles_with_floating_point_sides() {
153 let sides = [0.2, 0.2, 0.2];
154 let triangle = Triangle::build(sides).unwrap();
155 assert!(triangle.is_equilateral());
156 assert!(!triangle.is_scalene());
157}
158
159#[test]
160#[cfg(feature = "generic")]
161fn isosceles_triangle_with_floating_point_sides() {
162 let sides = [0.3, 0.4, 0.4];
163 let triangle = Triangle::build(sides).unwrap();
164 assert!(!triangle.is_equilateral());
165 assert!(triangle.is_isosceles());
166 assert!(!triangle.is_scalene());
167}
168
169#[test]
170#[cfg(feature = "generic")]
171fn invalid_triangle_with_floating_point_sides_one() {
172 let sides = [0.0, 0.4, 0.3];
173 let triangle = Triangle::build(sides);
174 assert!(triangle.is_none());
175}
176
177#[test]
178#[cfg(feature = "generic")]
179fn invalid_triangle_with_floating_point_sides_two() {
180 let sides = [0.1, 0.3, 0.5];
181 let triangle = Triangle::build(sides);
182 assert!(triangle.is_none());
183}