From 3e280892cb252e7fc3ea21b4b918dac3999152c2 Mon Sep 17 00:00:00 2001 From: Ian Whitney Date: Sun, 4 Sep 2016 20:23:37 -0500 Subject: [PATCH] Implement Triangle Test suite mostly follows the standard. I've started off with a couple of Result-checking tests, just to establish that we expect a Result back. Floating-point tests are included as optional, and an example that passes those tests is included. Making a function generic over Ints & Floats is tricky, and we decided to not make it a requirement. Hints file points to some optional ways of implementing a solution. A single `Triangle` struct gets the test passing fastest, but other designs can be explored. Since the single struct approach will be accessible to students by the time they reach rna-transcription, we've put it there. If we added tests that forced an Enum/Trait/Etc. impl, then we'd move it. But tests like that seemed too proscriptive. Full discussion of the implementation of this exercise is at https://github.com/exercism/xrust/pull/197 --- config.json | 1 + exercises/triangle/.gitignore | 7 ++ exercises/triangle/Cargo.toml | 3 + exercises/triangle/HINTS.md | 11 ++ exercises/triangle/example.rs | 38 ++++++ exercises/triangle/float_example.rs | 55 +++++++++ exercises/triangle/tests/triangle.rs | 170 +++++++++++++++++++++++++++ problems.md | 1 + 8 files changed, 286 insertions(+) create mode 100644 exercises/triangle/.gitignore create mode 100644 exercises/triangle/Cargo.toml create mode 100644 exercises/triangle/HINTS.md create mode 100644 exercises/triangle/example.rs create mode 100644 exercises/triangle/float_example.rs create mode 100644 exercises/triangle/tests/triangle.rs diff --git a/config.json b/config.json index 0775fa7fd..352c585c0 100644 --- a/config.json +++ b/config.json @@ -19,6 +19,7 @@ "etl", "sieve", "rna-transcription", + "triangle", "roman-numerals", "hexadecimal", "grade-school", diff --git a/exercises/triangle/.gitignore b/exercises/triangle/.gitignore new file mode 100644 index 000000000..cb14a4206 --- /dev/null +++ b/exercises/triangle/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# 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/exercises/triangle/Cargo.toml b/exercises/triangle/Cargo.toml new file mode 100644 index 000000000..bee41d36e --- /dev/null +++ b/exercises/triangle/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "triangle" +version = "0.0.0" diff --git a/exercises/triangle/HINTS.md b/exercises/triangle/HINTS.md new file mode 100644 index 000000000..5692bac32 --- /dev/null +++ b/exercises/triangle/HINTS.md @@ -0,0 +1,11 @@ +# Triangle in Rust + +- [Result](https://doc.rust-lang.org/std/result/index.html) + +Implementation of this can take many forms. Here are some topics that may help you, depending on the approach you take. + +- [Enums](https://doc.rust-lang.org/book/enums.html) +- [Traits](https://doc.rust-lang.org/book/traits.html) +- [BTreeSet](https://doc.rust-lang.org/std/collections/btree_set/struct.BTreeSet.html) + +Or maybe you will come up with an approach that uses none of those! diff --git a/exercises/triangle/example.rs b/exercises/triangle/example.rs new file mode 100644 index 000000000..2da70bdfa --- /dev/null +++ b/exercises/triangle/example.rs @@ -0,0 +1,38 @@ +use std::iter::FromIterator; +use std::collections::BTreeSet; + +pub struct Triangle { + sides: [u16; 3], +} + +impl Triangle { + #[cfg_attr(rustfmt, rustfmt_skip)] + fn valid_sides(&self) -> bool { + (self.sides.iter().all(|&s| s > 0)) && + (self.sides[0] + self.sides[1] >= self.sides[2]) && + (self.sides[1] + self.sides[2] >= self.sides[0]) && + (self.sides[2] + self.sides[0] >= self.sides[1]) + } + + pub fn build(sides: [u16; 3]) -> Result { + let t = Triangle { sides: sides }; + + if t.valid_sides() { + Ok(t) + } else { + Err(()) + } + } + + pub fn is_equilateral(&self) -> bool { + BTreeSet::from_iter(self.sides.iter()).len() == 1 + } + + pub fn is_isosceles(&self) -> bool { + BTreeSet::from_iter(self.sides.iter()).len() == 2 + } + + pub fn is_scalene(&self) -> bool { + BTreeSet::from_iter(self.sides.iter()).len() == 3 + } +} diff --git a/exercises/triangle/float_example.rs b/exercises/triangle/float_example.rs new file mode 100644 index 000000000..a415edbbc --- /dev/null +++ b/exercises/triangle/float_example.rs @@ -0,0 +1,55 @@ +use std::iter::FromIterator; +use std::collections::BTreeSet; + +extern crate num; + +use num::Num; + +pub struct Triangle { + sides: [T; 3], +} + +impl Triangle { + #[cfg_attr(rustfmt, rustfmt_skip)] + fn valid_sides(&self) -> bool { + let z = self.sides[0] - self.sides[0]; + (self.sides.iter().all(|&s| s > z)) && + (self.sides[0] + self.sides[1] >= self.sides[2]) && + (self.sides[1] + self.sides[2] >= self.sides[0]) && + (self.sides[2] + self.sides[0] >= self.sides[1]) + } + + pub fn build(sides: [T; 3]) -> Result { + let t = Triangle { sides: sides }; + + if t.valid_sides() { + Ok(t) + } else { + Err(()) + } + } + + pub fn is_equilateral(&self) -> bool { + let a = self.sides[0]; + let b = self.sides[1]; + let c = self.sides[2]; + + a == b && b == c + } + + pub fn is_isosceles(&self) -> bool { + let a = self.sides[0]; + let b = self.sides[1]; + let c = self.sides[2]; + + (a == b && a != c) || (a == c && a != b) || (b == c && a != b) + } + + pub fn is_scalene(&self) -> bool { + let a = self.sides[0]; + let b = self.sides[1]; + let c = self.sides[2]; + + a != b && a != c && b != c + } +} diff --git a/exercises/triangle/tests/triangle.rs b/exercises/triangle/tests/triangle.rs new file mode 100644 index 000000000..1e108462c --- /dev/null +++ b/exercises/triangle/tests/triangle.rs @@ -0,0 +1,170 @@ +extern crate triangle; + +use triangle::*; + +#[test] +fn positive_length_sides_are_ok() { + let sides = [2, 2, 2]; + let triangle = Triangle::build(sides); + assert!(triangle.is_ok()); +} + +#[test] +#[ignore] +fn zero_length_sides_are_illegal() { + let sides = [0, 0, 0]; + let triangle = Triangle::build(sides); + assert!(triangle.is_err()); +} + +#[test] +#[ignore] +fn equilateral_triangles_have_equal_sides() { + let sides = [2, 2, 2]; + let triangle = Triangle::build(sides).unwrap(); + assert!(triangle.is_equilateral()); + assert!(!triangle.is_isosceles()); + assert!(!triangle.is_scalene()); +} + +#[test] +#[ignore] +fn larger_equilateral_triangles_have_equal_sides() { + let sides = [10, 10, 10]; + let triangle = Triangle::build(sides).unwrap(); + assert!(triangle.is_equilateral()); + assert!(!triangle.is_isosceles()); + assert!(!triangle.is_scalene()); +} + +#[test] +#[ignore] +fn isocseles_triangles_have_two_equal_sides_one() { + let sides = [3, 4, 4]; + let triangle = Triangle::build(sides).unwrap(); + assert!(!triangle.is_equilateral()); + assert!(triangle.is_isosceles()); + assert!(!triangle.is_scalene()); +} + +#[test] +#[ignore] +fn isocseles_triangles_have_two_equal_sides_two() { + let sides = [4, 4, 3]; + let triangle = Triangle::build(sides).unwrap(); + assert!(!triangle.is_equilateral()); + assert!(triangle.is_isosceles()); + assert!(!triangle.is_scalene()); +} + +#[test] +#[ignore] +fn isocseles_triangles_have_two_equal_sides_three() { + let sides = [4, 3, 4]; + let triangle = Triangle::build(sides).unwrap(); + assert!(!triangle.is_equilateral()); + assert!(triangle.is_isosceles()); + assert!(!triangle.is_scalene()); +} + +#[test] +#[ignore] +fn isocseles_triangles_have_two_equal_sides_four() { + let sides = [4, 7, 4]; + let triangle = Triangle::build(sides).unwrap(); + assert!(!triangle.is_equilateral()); + assert!(triangle.is_isosceles()); + assert!(!triangle.is_scalene()); +} + +#[test] +#[ignore] +fn scalene_triangle_has_no_equal_sides_one() { + let sides = [3, 4, 5]; + let triangle = Triangle::build(sides).unwrap(); + assert!(!triangle.is_equilateral()); + assert!(!triangle.is_isosceles()); + assert!(triangle.is_scalene()); +} + +#[test] +#[ignore] +fn scalene_triangle_has_no_equal_sides_two() { + let sides = [5, 4, 6]; + let triangle = Triangle::build(sides).unwrap(); + assert!(!triangle.is_equilateral()); + assert!(!triangle.is_isosceles()); + assert!(triangle.is_scalene()); +} + +#[test] +#[ignore] +fn scalene_triangle_has_no_equal_sides_three() { + let sides = [10, 11, 12]; + let triangle = Triangle::build(sides).unwrap(); + assert!(!triangle.is_equilateral()); + assert!(!triangle.is_isosceles()); + assert!(triangle.is_scalene()); +} + +#[test] +#[ignore] +fn scalene_triangle_has_no_equal_sides_four() { + let sides = [5, 4, 2]; + let triangle = Triangle::build(sides).unwrap(); + assert!(!triangle.is_equilateral()); + assert!(!triangle.is_isosceles()); + assert!(triangle.is_scalene()); +} + +#[test] +#[ignore] +fn sum_of_two_sides_must_equal_or_exceed_the_remaining_side_one() { + let sides = [7, 3, 2]; + let triangle = Triangle::build(sides); + assert!(triangle.is_err()); +} + +#[test] +#[ignore] +fn sum_of_two_sides_must_equal_or_exceed_the_remaining_side_two() { + let sides = [1, 1, 3]; + let triangle = Triangle::build(sides); + assert!(triangle.is_err()); +} + +// Optional Tests +// +// Support Triangles with non-integer sides. +// +// You'll probably want to use the Num crate +// +// https://crates.io/crates/num +// + +// #[test] +// fn scalene_triangle_with_floating_point_sides() { +// let sides = [0.4, 0.6, 0.3]; +// let triangle = Triangle::build(sides).unwrap(); +// assert!(!triangle.is_equilateral()); +// assert!(!triangle.is_isosceles()); +// assert!(triangle.is_scalene()); +// } +// +// #[test] +// fn equilateral_triangles_with_floating_point_sides() { +// let sides = [0.2, 0.2, 0.2]; +// let triangle = Triangle::build(sides).unwrap(); +// assert!(triangle.is_equilateral()); +// assert!(!triangle.is_isosceles()); +// assert!(!triangle.is_scalene()); +// } +// +// #[test] +// fn isocseles_triangle_with_floating_point_sides() { +// let sides = [0.3, 0.4, 0.4]; +// let triangle = Triangle::build(sides).unwrap(); +// assert!(!triangle.is_equilateral()); +// assert!(triangle.is_isosceles()); +// assert!(!triangle.is_scalene()); +// } diff --git a/problems.md b/problems.md index 2e3938446..868c9127e 100644 --- a/problems.md +++ b/problems.md @@ -38,6 +38,7 @@ word-count | hashmap, str vs string, chars, entry api etl | btree sieve | vector, map, while let (optional) rna-transcription | match, struct, str vs string +triangle | Math, Struct. Enum, Trait, Box -- depending on implementation roman-numerals | mutable, results, loops, struct, traits hexadecimal | Option, zip/fold/chars, map grade-school | struct, entry api, Vec, Option