From 796419c5ce22bbcf6688ab857adac0b9e0e890fe Mon Sep 17 00:00:00 2001 From: devkabiir <18462563+devkabiir@users.noreply.github.com> Date: Mon, 10 Apr 2023 19:52:09 +0530 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=20(concepts)=20Add=20concept=20exe?= =?UTF-8?q?rcise=20`interest-is-interesting`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is inspired by the same on csharp track. Provides introduction to while loops & floating point numbers. --- config.json | 15 ++ .../interest-is-interesting/.docs/hints.md | 38 ++++ .../.docs/instructions.md | 58 +++++ .../.docs/introduction.md | 38 ++++ .../interest-is-interesting/.gitignore | 8 + .../interest-is-interesting/.meta/config.json | 21 ++ .../interest-is-interesting/.meta/design.md | 49 +++++ .../interest-is-interesting/.meta/exemplar.rs | 35 +++ .../interest-is-interesting/Cargo.toml | 4 + .../interest-is-interesting/src/lib.rs | 15 ++ .../tests/interest-is-interesting.rs | 206 ++++++++++++++++++ 11 files changed, 487 insertions(+) create mode 100644 exercises/concept/interest-is-interesting/.docs/hints.md create mode 100644 exercises/concept/interest-is-interesting/.docs/instructions.md create mode 100644 exercises/concept/interest-is-interesting/.docs/introduction.md create mode 100644 exercises/concept/interest-is-interesting/.gitignore create mode 100644 exercises/concept/interest-is-interesting/.meta/config.json create mode 100644 exercises/concept/interest-is-interesting/.meta/design.md create mode 100644 exercises/concept/interest-is-interesting/.meta/exemplar.rs create mode 100644 exercises/concept/interest-is-interesting/Cargo.toml create mode 100644 exercises/concept/interest-is-interesting/src/lib.rs create mode 100644 exercises/concept/interest-is-interesting/tests/interest-is-interesting.rs diff --git a/config.json b/config.json index 5609f8e10..3e8a7cc87 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-statemetns" + ], + "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..981af4e9d --- /dev/null +++ b/exercises/concept/interest-is-interesting/.docs/hints.md @@ -0,0 +1,38 @@ +# Hints + +## General + +- [Floating-point numeric types introduction][docs.microsoft.com-floating_point_numeric_types]. + +## 1. Calculate the interest rate + +- By default, any floating-point number defined in Rust code is treated as a + `f64`. +- To use `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 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..47a39465c --- /dev/null +++ b/exercises/concept/interest-is-interesting/.docs/instructions.md @@ -0,0 +1,58 @@ +# 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_is_interesting::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_is_interesting::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 +interest_is_interesting::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 +interest_is_interesting::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..edc308bad --- /dev/null +++ b/exercises/concept/interest-is-interesting/.docs/introduction.md @@ -0,0 +1,38 @@ +# 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). + +## While Loops + +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 = 23; + +while x > 10 { + // Execute logic if x > 10 + x = x - 2; +} +``` 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..88864f5b8 --- /dev/null +++ b/exercises/concept/interest-is-interesting/.meta/exemplar.rs @@ -0,0 +1,35 @@ +pub fn interest_rate(balance: f64) -> f64 { + if balance < 0.0 { + return 3.213; + } + + if balance < 1000.0 { + return 0.5; + } + + if balance < 5000.0 { + return 1.621; + } + + return 2.475; +} + +pub fn interest(balance: f64) -> f64 { + return balance * interest_rate(balance) / 100.0; +} + +pub fn annual_balance_update(balance: f64) -> f64 { + return 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; + } + + return 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) + ); +}