-
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add Calculator conundrum concept exercise (#307)
* non-working scaffold * Implement exercise + update prerequisites * recreate exercise + add design.md + add panic to exerc * division panics * add hints * add introduction * add instructions * remove ? section * make operation simple ByteArray (no Option) * fix hints * Lint markdown * Apply suggestions from code review Co-authored-by: András B Nagy <[email protected]> --------- Co-authored-by: András B Nagy <[email protected]>
- Loading branch information
Showing
21 changed files
with
288 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Hints | ||
|
||
## General | ||
|
||
- [Unrecoverable Errors][unrecoverable]: invoke a panic with error message. | ||
- [`panic!` Macro][panic-excl-macro]: invoke a panic with `ByteArray` error message. | ||
- [`assert!` Macro][assert]: invoke a panic if a condition evaluates to `false`. | ||
- [Result Enum][result]: how to use the `Result` enum. | ||
|
||
## 2. Handle illegal operations | ||
|
||
- You need to [return an error][result] here. | ||
|
||
## 3. Handle no operation provided | ||
|
||
- You need to [panic][panic-excl-macro] here with a `ByteArray` message to handle empty strings for operations. | ||
|
||
## 4. Handle errors when dividing by zero | ||
|
||
- You need to panic here to handle division by zero. | ||
|
||
[unrecoverable]: https://book.cairo-lang.org/ch09-01-unrecoverable-errors-with-panic.html#unrecoverable-errors-with-panic | ||
[panic-excl-macro]: https://book.cairo-lang.org/ch09-01-unrecoverable-errors-with-panic.html#panic-macro | ||
[assert]: https://book.cairo-lang.org/ch11-05-macros.html?highlight=assert#assert-and-assert_xx-macros | ||
[result]: https://book.cairo-lang.org/ch09-02-recoverable-errors.html#the-result-enum |
46 changes: 46 additions & 0 deletions
46
exercises/concept/calculator-conundrum/.docs/instructions.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Instructions | ||
|
||
In this exercise, you will be building error handling for a simple integer calculator. | ||
The calculator should support addition, multiplication, and division operations, returning the result as a formatted string. | ||
You will also implement error handling to address illegal operations and division by zero. | ||
|
||
The goal is to have a working calculator that returns a string in the following format: | ||
|
||
```rust | ||
SimpleCalculator::calculate(16, 51, "+"); // => returns "16 + 51 = 67" | ||
|
||
SimpleCalculator::calculate(32, 6, "*"); // => returns "32 * 6 = 192" | ||
|
||
SimpleCalculator::calculate(512, 4, "/"); // => returns "512 / 4 = 128" | ||
``` | ||
|
||
## 1. Implement the calculator operations | ||
|
||
The main function for this task will be `SimpleCalculator::calculate`, which takes three arguments: two integers and a `ByteArray` representing the operation. | ||
Implement the following operations: | ||
|
||
- **Addition** with the `+` symbol | ||
- **Multiplication** with the `*` symbol | ||
- **Division** with the `/` symbol | ||
|
||
## 2. Handle illegal operations | ||
|
||
If the operation symbol is anything other than `+`, `*`, or `/`, the calculator should either panic or return an error: | ||
|
||
- If the operation is an empty string, panic with a `ByteArray` error message `"Operation cannot be an empty string"`. | ||
- For any other invalid operation, return `Result::Err("Operation is out of range")`. | ||
|
||
```rust | ||
SimpleCalculator::calculate(100, 10, "-"); // => returns Result::Err("Operation is out of range") | ||
|
||
SimpleCalculator::calculate(8, 2, ""); // => panics with "Operation cannot be an empty string" | ||
``` | ||
|
||
## 3. Handle errors when dividing by zero | ||
|
||
When attempting to divide by `0`, the calculator should panic with an error message indicating that division by zero is not allowed. | ||
The returned result should be a `felt252` value of `'Division by zero is not allowed'`. | ||
|
||
```rust | ||
SimpleCalculator::calculate(512, 0, "/"); // => panics with 'Division by zero is not allowed' | ||
``` |
65 changes: 65 additions & 0 deletions
65
exercises/concept/calculator-conundrum/.docs/introduction.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Introduction | ||
|
||
In programming, it's essential to handle errors gracefully to ensure that unexpected situations do not cause a program to crash or behave unpredictably. | ||
Cairo provides two main mechanisms for error handling: | ||
|
||
1. **Unrecoverable errors** with `panic`, which immediately stop the program. | ||
2. **Recoverable errors** with `Result`, which allow the program to handle and respond to errors. | ||
|
||
## Unrecoverable Errors with `panic` | ||
|
||
Sometimes, a program encounters an error so severe that it cannot proceed. | ||
Cairo uses the `panic` function to immediately stop execution in such cases. | ||
This is helpful when an error, like attempting to access an out-of-bounds index, makes it impossible for the program to continue in a sensible way. | ||
The `panic` function accepts a `ByteArray` message that describes the reason for the error. | ||
|
||
For example, in Cairo: | ||
|
||
```rust | ||
fn main() { | ||
let data = array![1, 2]; | ||
panic("An unrecoverable error has occurred!"); | ||
} | ||
``` | ||
|
||
This example demonstrates a forced panic, which immediately stops the program with a message. | ||
|
||
### The `assert!` Macro | ||
|
||
The `assert!` macro is a useful tool for enforcing specific conditions in your code. | ||
If the condition in `assert!` evaluates to `false`, the program will panic with a `ByteArray` error message. | ||
This is often used to verify assumptions during development and ensure values meet certain criteria. | ||
|
||
For example: | ||
|
||
```rust | ||
fn main() { | ||
let x = 5; | ||
assert!(x > 0, "x must be greater than zero"); | ||
} | ||
``` | ||
|
||
If `x` is not greater than zero, the program will panic with the message `"x must be greater than zero"`. `assert!` is helpful for checking invariants and preconditions without manually writing error-handling code. | ||
|
||
## Recoverable Errors with `Result` | ||
|
||
Not all errors need to stop the program. | ||
Some can be handled gracefully so the program can continue. | ||
Cairo's `Result` enum represents these recoverable errors, and it has two variants: | ||
|
||
- `Result::Ok` indicates success. | ||
- `Result::Err` represents an error. | ||
|
||
Using `Result`, a function can return either a success value or an error, allowing the calling function to decide what to do next. | ||
|
||
```rust | ||
fn divide(a: u32, b: u32) -> Result<u32, ByteArray> { | ||
if b == 0 { | ||
Result::Err("Error: Division by zero") | ||
} else { | ||
Result::Ok(a / b) | ||
} | ||
} | ||
``` | ||
|
||
In this example, if `b` is zero, an error is returned; otherwise, the result of the division is returned. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Design | ||
|
||
## Goal | ||
|
||
Introduce the student to error handling in Cairo. | ||
|
||
## Learning objectives | ||
|
||
- know how create recoverable errors. | ||
- know how create unrecoverable errors. | ||
|
||
## Out of scope | ||
|
||
## Concepts | ||
|
||
- error-handling | ||
|
||
## Prerequisites | ||
|
||
- traits | ||
- option | ||
|
||
## Resources to refer to | ||
|
||
- [Cairo Book - Error Handling][error-handling] | ||
|
||
[error-handling]: https://book.cairo-lang.org/ch09-00-error-handling.html |
17 changes: 17 additions & 0 deletions
17
exercises/concept/calculator-conundrum/.meta/exemplar.cairo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#[generate_trait] | ||
pub impl SimpleCalculatorImpl of SimpleCalculatorTrait { | ||
fn calculate(a: i32, b: i32, operation: ByteArray) -> Result<ByteArray, ByteArray> { | ||
assert!(operation != "", "Operation cannot be an empty string"); | ||
|
||
if operation == "+" { | ||
Result::Ok(format!("{} + {} = {}", a, b, a + b)) | ||
} else if operation == "*" { | ||
Result::Ok(format!("{} * {} = {}", a, b, a * b)) | ||
} else if operation == "/" { | ||
assert(b != 0, 'Division by zero is not allowed'); | ||
Result::Ok(format!("{} / {} = {}", a, b, a / b)) | ||
} else { | ||
Result::Err("Operation is out of range") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[package] | ||
name = "calculator_conundrum" | ||
version = "0.1.0" | ||
edition = "2024_07" | ||
|
||
[dev-dependencies] | ||
cairo_test = "2.8.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#[generate_trait] | ||
pub impl SimpleCalculatorImpl of SimpleCalculatorTrait { | ||
fn calculate(a: i32, b: i32, operation: ByteArray) -> Result<ByteArray, ByteArray> { | ||
panic!("implement `calculate`") | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
exercises/concept/calculator-conundrum/tests/calculator_conundrum.cairo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
use calculator_conundrum::SimpleCalculatorTrait as SimpleCalculator; | ||
|
||
#[test] | ||
fn addition_with_small_operands() { | ||
assert_eq!(SimpleCalculator::calculate(22, 25, "+").unwrap(), "22 + 25 = 47"); | ||
} | ||
|
||
#[test] | ||
#[ignore] | ||
fn addition_with_large_operands() { | ||
assert_eq!( | ||
SimpleCalculator::calculate(378_961, 399_635, "+").unwrap(), "378961 + 399635 = 778596" | ||
); | ||
} | ||
|
||
#[test] | ||
#[ignore] | ||
fn multiplication_with_small_operands() { | ||
assert_eq!(SimpleCalculator::calculate(3, 21, "*").unwrap(), "3 * 21 = 63"); | ||
} | ||
|
||
#[test] | ||
#[ignore] | ||
fn multiplication_with_large_operands() { | ||
assert_eq!( | ||
SimpleCalculator::calculate(72_441, 2_048, "*").unwrap(), "72441 * 2048 = 148359168" | ||
); | ||
} | ||
|
||
#[test] | ||
#[ignore] | ||
fn division_with_small_operands() { | ||
assert_eq!(SimpleCalculator::calculate(72, 9, "/").unwrap(), "72 / 9 = 8"); | ||
} | ||
|
||
#[test] | ||
#[ignore] | ||
fn division_with_large_operands() { | ||
assert_eq!( | ||
SimpleCalculator::calculate(1_338_800, 83_675, "/").unwrap(), "1338800 / 83675 = 16" | ||
); | ||
} | ||
|
||
#[test] | ||
#[ignore] | ||
fn calculate_returns_result_err_for_non_valid_operations() { | ||
assert_eq!(SimpleCalculator::calculate(1, 2, "**").unwrap_err(), "Operation is out of range"); | ||
} | ||
|
||
#[test] | ||
#[ignore] | ||
#[should_panic(expected: ("Operation cannot be an empty string",))] | ||
fn calculate_returns_result_err_for_empty_string_as_operation() { | ||
let _ = SimpleCalculator::calculate(1, 2, ""); | ||
} | ||
|
||
#[test] | ||
#[ignore] | ||
#[should_panic(expected: ('Division by zero is not allowed',))] | ||
fn calculate_panics_for_division_with_0() { | ||
let _ = SimpleCalculator::calculate(33, 0, "/"); | ||
} |
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters