diff --git a/config.json b/config.json index 5609f8e10..5d975f9cd 100644 --- a/config.json +++ b/config.json @@ -35,6 +35,21 @@ }, "exercises": { "concept": [ + { + "slug": "interest-is-interesting", + "uuid": "8a81dfe7-e941-4f94-895d-2f2b8305153d", + "name": "Interest is Interesting", + "difficulty": 1, + "concepts": [ + "floating-point-numbers", + "while-loops" + ], + "prerequisites": [ + "assignment", + "if-statements" + ], + "status": "wip" + }, { "slug": "lucians-luscious-lasagna", "uuid": "29a2d3bd-eec8-454d-9dba-4b2d7d071925", diff --git a/exercises/concept/interest-is-interesting/.docs/hints.md b/exercises/concept/interest-is-interesting/.docs/hints.md new file mode 100644 index 000000000..ac10f1fea --- /dev/null +++ b/exercises/concept/interest-is-interesting/.docs/hints.md @@ -0,0 +1,42 @@ +# Hints + +## General + +- [Floating-point types][floating-point-types] section on Chapter 3.2 Data Types + of the Rust Book. + +## 1. Calculate the interest rate + +- By default, any floating-point number defined in Rust code is treated as a + [`f64`][f64]. +- To use [`f32`][f32] one can write numbers with a suffix of `_f32` or explicitly add + the type to declaration. + ```rust + let x = 2.0_f32; + + let y: f32 = 3.0; + ``` +- [If statements][if-statements] can be used to return different values based on certain + conditions. + + +## 2. Calculate the interest + +- When calculating interest, it might be helpful to notice that `interest_rate` returns a percentage. + +## 3. Calculate the annual balance update + +- When calculating the annual balance update, we can use methods we have defined in previous steps. + +## 4. Calculate the years before reaching the desired balance + +- To calculate the years, one can keep looping until the desired balance is + reached. You can use the [while-loop]. + +[while-loop]: https://doc.rust-lang.org/rust-by-example/flow_control/while.html +[f32]: https://doc.rust-lang.org/std/primitive.f32.html +[f64]: https://doc.rust-lang.org/std/primitive.f64.html +[if-statements]: https://doc.rust-lang.org/book/ch03-05-control-flow.html#if-expressions +[floating-point-types]: + https://doc.rust-lang.org/book/ch03-02-data-types.html?highlight=floating#floating-point-types +[rust-book]: https://doc.rust-lang.org/book diff --git a/exercises/concept/interest-is-interesting/.docs/instructions.md b/exercises/concept/interest-is-interesting/.docs/instructions.md new file mode 100644 index 000000000..c8fea0bc3 --- /dev/null +++ b/exercises/concept/interest-is-interesting/.docs/instructions.md @@ -0,0 +1,59 @@ +# Instructions + +In this exercise you'll be working with savings accounts. Each year, the balance of your savings account is updated based on its interest rate. +The interest rate your bank gives you depends on the amount of money in your account (its balance): + +- 3.213% for a negative balance (balance gets more negative). +- 0.5% for a positive balance less than `1000` dollars. +- 1.621% for a positive balance greater than or equal to `1000` dollars and less than `5000` dollars. +- 2.475% for a positive balance greater than or equal to `5000` dollars. + +You have four tasks, each of which will deal your balance and its interest rate. + +## 1. Calculate the interest rate + +Implement the `interest_rate()` method to calculate the interest rate based on the specified balance: + +```rust +interest_rate(200.75) +// 0.5 +``` + + +## 2. Calculate the interest + +Implement the `interest()` method to calculate the interest based on the specified balance: + +```rust +interest(200.75) +// 1.00375 +``` + + +## 3. Calculate the annual balance update + +Implement the `annual_balance_update()` method to calculate the annual balance update, taking into account the interest rate: + +```rust +annual_balance_update(200.75) +// 201.75375 +``` + + +## 4. Calculate the years before reaching the desired balance + +Implement the `years_before_desired_balance()` method to calculate the minimum number of years required to reach the desired balance given annually compounding interest: + +```rust +years_before_desired_balance(200.75, 214.88) +// 14 +``` + +Note that the value returned is an integer. + +~~~~exercism/note +When applying simple interest to a principal balance, the balance is multiplied by the interest rate and the product of the two is the interest amount. + +Compound interest on the other hand is done by applying interest on a recurring basis. +On each application the interest amount is computed and added to the principal balance so that subsequent interest calculations are subject to a greater principal balance. +~~~~ diff --git a/exercises/concept/interest-is-interesting/.docs/introduction.md b/exercises/concept/interest-is-interesting/.docs/introduction.md new file mode 100644 index 000000000..17230bd04 --- /dev/null +++ b/exercises/concept/interest-is-interesting/.docs/introduction.md @@ -0,0 +1,45 @@ +# Introduction + +## Floating Point Numbers + +A floating-point number is a number with zero or more digits behind the decimal +separator. +Examples are `-2.4`, `0.1`, `3.14`, `16.984025` and `1024.0`. + +Different floating-point types can store different numbers of digits after the digit separator - this is referred to as its precision. + +Rust implements the IEEE 754-2008 "binary32" and "binary64" floating-point types as `f32` and `f64`, respectively. +The f32 type is a single-precision float, and f64 has double precision. + +- `f32`: 32 bit floating point precision. Written as `2.45_f32`. +- `f64`: 64 bit floating point precision. This is default in rust. Written as + `2.45_f64`. + +```rust +fn main() { + let x = 2.0; // f64 + + let y: f32 = 3.0; // f32 +} +``` + +As can be seen, each type can store a different number of digits. +This means that trying to store PI in a `float` will only store the first 6 to 9 digits (with the last digit being rounded). + +## Conditional Loops with while + +In this exercise you may also want to use a loop. +There are several ways to write loops in Rust, but the `while` loop is most appropriate here: + +```rust +let mut x = 10; + +while x > 0 { + // Execute logic if x > 10 + x = x - 1; +} +``` + +In the above example, we define a `while` loop with the condition `x > 0`. +Since the initial value of `x` was set to `10` and we decrement it by 1 every +loop, this will run the code inside the curly braces 10 times. diff --git a/exercises/concept/interest-is-interesting/.gitignore b/exercises/concept/interest-is-interesting/.gitignore new file mode 100644 index 000000000..db7f315c0 --- /dev/null +++ b/exercises/concept/interest-is-interesting/.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/exercises/concept/interest-is-interesting/.meta/config.json b/exercises/concept/interest-is-interesting/.meta/config.json new file mode 100644 index 000000000..1b4d92a62 --- /dev/null +++ b/exercises/concept/interest-is-interesting/.meta/config.json @@ -0,0 +1,21 @@ +{ + "authors": [ + "devkabiir" + ], + "forked_from": [ + "csharp/interest-is-interesting" + ], + "files": { + "solution": [ + "src/lib.rs", + "Cargo.toml" + ], + "test": [ + "tests/interest-is-interesting.rs" + ], + "exemplar": [ + ".meta/exemplar.rs" + ] + }, + "blurb": "Learn about floating point numbers by adding interest to savings accounts." +} diff --git a/exercises/concept/interest-is-interesting/.meta/design.md b/exercises/concept/interest-is-interesting/.meta/design.md new file mode 100644 index 000000000..952cfa54f --- /dev/null +++ b/exercises/concept/interest-is-interesting/.meta/design.md @@ -0,0 +1,49 @@ +# Design + +## Learning objectives + +- Know how number literals are represented in Rust +- Know the different types of integer and floating point primitives +- Know how to round floats +- Know how to write while loops +- Know how to use if statements + +## Out of scope + +- High precision floating point arithmetic. +- Decimals and crates that provide such. + +## Concepts + +- Numbers +- Integers +- Floating point values (basic) +- If statements +- While loops + +## Prerequisites + +None + +## Resources to refer to + +### Hints + +[Rust book - Scalar types](https://doc.rust-lang.org/stable/book/ch03-02-data-types.html?highlight=primitive#scalar-types) +[cheats.rs - Basic Types](https://cheats.rs/#basic-types) +[Rust reference - Integer literals](https://doc.rust-lang.org/stable/reference/tokens.html#integer-literals) +[Rust reference - Floating point literals](https://doc.rust-lang.org/stable/reference/tokens.html#floating-point-literals) + +### After + +[Rust reference - Literals](https://doc.rust-lang.org/stable/reference/expressions/literal-expr.html) +[Rust reference - Numeric types](https://doc.rust-lang.org/stable/reference/types/numeric.html) +[Rust reference - Type cast expressions](https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#type-cast-expressions) + +## Representer + +This exercise does not require any specific representation logic to be added to the representer. + +## Analyzer + +This exercise does not require any specific logic to be added to the analyzer. diff --git a/exercises/concept/interest-is-interesting/.meta/exemplar.rs b/exercises/concept/interest-is-interesting/.meta/exemplar.rs new file mode 100644 index 000000000..43a5ad17e --- /dev/null +++ b/exercises/concept/interest-is-interesting/.meta/exemplar.rs @@ -0,0 +1,31 @@ +pub fn interest_rate(balance: f64) -> f64 { + if balance < 0.0 { + 3.213 + } else if balance < 1000.0 { + 0.5 + } else if balance < 5000.0 { + 1.621 + } else { + 2.475 + } +} + +pub fn interest(balance: f64) -> f64 { + balance * interest_rate(balance) / 100.0 +} + +pub fn annual_balance_update(balance: f64) -> f64 { + balance + interest(balance) +} + +pub fn years_before_desired_balance(balance: f64, target_balance: f64) -> u8 { + let mut accumulating_balance = balance; + let mut years = 0; + + while accumulating_balance < target_balance { + accumulating_balance = annual_balance_update(accumulating_balance); + years += 1; + } + + years +} diff --git a/exercises/concept/interest-is-interesting/Cargo.toml b/exercises/concept/interest-is-interesting/Cargo.toml new file mode 100644 index 000000000..8a21debaa --- /dev/null +++ b/exercises/concept/interest-is-interesting/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "interest_is_interesting" +version = "0.1.0" +edition = "2021" diff --git a/exercises/concept/interest-is-interesting/src/lib.rs b/exercises/concept/interest-is-interesting/src/lib.rs new file mode 100644 index 000000000..94ccc87b0 --- /dev/null +++ b/exercises/concept/interest-is-interesting/src/lib.rs @@ -0,0 +1,15 @@ +pub fn interest_rate(_balance: f64) -> f64 { + unimplemented!("Implement interest_rate") +} + +pub fn interest(_balance: f64) -> f64 { + unimplemented!("Implement interest") +} + +pub fn annual_balance_update(_balance: f64) -> f64 { + unimplemented!("Implement annual_balance_update") +} + +pub fn years_before_desired_balance(_balance: f64, _target_balance: f64) -> u8 { + unimplemented!("Implement years_before_desired_balance") +} diff --git a/exercises/concept/interest-is-interesting/tests/interest-is-interesting.rs b/exercises/concept/interest-is-interesting/tests/interest-is-interesting.rs new file mode 100644 index 000000000..b6142a8a3 --- /dev/null +++ b/exercises/concept/interest-is-interesting/tests/interest-is-interesting.rs @@ -0,0 +1,206 @@ +fn round_to_precision(input: f64, digits: u32) -> f64 { + let precision = (10_i32.pow(digits)) as f64; + let mut result = input * precision; + result = result.round(); + result /= precision; + result +} + +#[test] +pub fn minimal_first_interest_rate() { + assert_eq!(0.5, interest_is_interesting::interest_rate(0.0)); +} + +#[test] +#[ignore] +pub fn tiny_first_interest_rate() { + assert_eq!(0.5, interest_is_interesting::interest_rate(0.000_001)); +} + +#[test] +#[ignore] +pub fn maximum_first_interest_rate() { + assert_eq!(0.5, interest_is_interesting::interest_rate(999.9999)); +} + +#[test] +#[ignore] +pub fn minimal_second_interest_rate() { + assert_eq!(1.621, interest_is_interesting::interest_rate(1_000.0)); +} + +#[test] +#[ignore] +pub fn tiny_second_interest_rate() { + assert_eq!(1.621, interest_is_interesting::interest_rate(1_000.000_1)); +} + +#[test] +#[ignore] +pub fn maximum_second_interest_rate() { + assert_eq!(1.621, interest_is_interesting::interest_rate(4_999.999_0)); +} + +#[test] +#[ignore] +pub fn minimal_third_interest_rate() { + assert_eq!(2.475, interest_is_interesting::interest_rate(5_000.000_0)); +} + +#[test] +#[ignore] +pub fn tiny_third_interest_rate() { + assert_eq!(2.475, interest_is_interesting::interest_rate(5_000.000_1)); +} + +#[test] +#[ignore] +pub fn large_third_interest_rate() { + assert_eq!( + 2.475, + interest_is_interesting::interest_rate(5_639_998.742_909) + ); +} + +#[test] +#[ignore] +pub fn rate_on_minimal_negative_balance() { + assert_eq!(3.213, interest_is_interesting::interest_rate(-0.000_001)); +} + +#[test] +#[ignore] +pub fn rate_on_small_negative_balance() { + assert_eq!(3.213, interest_is_interesting::interest_rate(-0.123)); +} + +#[test] +#[ignore] +pub fn rate_on_regular_negative_balance() { + assert_eq!(3.213, interest_is_interesting::interest_rate(-300.0)); +} + +#[test] +#[ignore] +pub fn rate_on_large_negative_balance() { + assert_eq!(3.213, interest_is_interesting::interest_rate(-152_964.231)); +} + +#[test] +#[ignore] +pub fn interest_on_negative_balance() { + assert_eq!(-321.3, interest_is_interesting::interest(-10_000.0)); +} + +#[test] +#[ignore] +pub fn interest_on_small_balance() { + let actual = interest_is_interesting::interest(555.55); + assert_eq!(2.777_75, round_to_precision(actual, 5)); +} + +#[test] +#[ignore] +pub fn interest_on_medium_balance() { + let actual = interest_is_interesting::interest(4999.99); + assert_eq!(81.049_84, round_to_precision(actual, 5)); +} + +#[test] +#[ignore] +pub fn interest_on_large_balance() { + let actual = interest_is_interesting::interest(34600.80); + assert_eq!(856.3698, round_to_precision(actual, 4)); +} + +#[test] +#[ignore] +pub fn annual_balance_update_for_empty_start_balance() { + assert_eq!(0.0000, interest_is_interesting::annual_balance_update(0.0)); +} + +#[test] +#[ignore] +pub fn annual_balance_update_for_small_positive_start_balance() { + assert_eq!( + 0.000_001_005, + interest_is_interesting::annual_balance_update(0.000_001) + ); +} + +#[test] +#[ignore] +pub fn annual_balance_update_for_average_positive_start_balance() { + assert_eq!( + 1_016.210_000, + interest_is_interesting::annual_balance_update(1_000.0) + ); +} + +#[test] +#[ignore] +pub fn annual_balance_update_for_large_positive_start_balance() { + let actual = interest_is_interesting::annual_balance_update(1_000.000_1); + assert_eq!(1_016.210_1, round_to_precision(actual, 4)); +} + +#[test] +#[ignore] +pub fn annual_balance_update_for_huge_positive_start_balance() { + let actual = interest_is_interesting::annual_balance_update(898_124_017.826_243_4); + assert_eq!(920_352_587.267_443, actual); +} + +#[test] +#[ignore] +pub fn annual_balance_update_for_small_negative_start_balance() { + assert_eq!( + -0.126_951_99, + interest_is_interesting::annual_balance_update(-0.123) + ); +} + +#[test] +#[ignore] +pub fn annual_balance_update_for_large_negative_start_balance() { + assert_eq!( + -157_878.971_742_03, + interest_is_interesting::annual_balance_update(-152_964.231) + ); +} + +#[test] +#[ignore] +pub fn years_before_desired_balance_for_small_start_balance() { + assert_eq!( + 47, + interest_is_interesting::years_before_desired_balance(100.0, 125.80) + ); +} + +#[test] +#[ignore] +pub fn years_before_desired_balance_for_average_start_balance() { + assert_eq!( + 6, + interest_is_interesting::years_before_desired_balance(1_000.0, 1_100.0) + ); +} + +#[test] +#[ignore] +pub fn years_before_desired_balance_for_large_start_balance() { + assert_eq!( + 5, + interest_is_interesting::years_before_desired_balance(8_080.80, 9_090.90) + ); +} + +#[test] +#[ignore] +pub fn years_before_desired_balance_for_large_different_between_start_and_target_balance() { + assert_eq!( + 85, + interest_is_interesting::years_before_desired_balance(2_345.67, 12_345.678_9) + ); +}