Skip to content

Commit

Permalink
Add the chrono-realms-chrono-chain Exercise (#322)
Browse files Browse the repository at this point in the history
* add introduction.md

* update descr in control-flow links

* add missing comma in control-flow blurb

* Add smart pointers concept

* rewrite introduction.md

* add hints

* write blurb

* format md files

* add design

* update link in hints

* add more tests + update to be trait-based

* Update config.json prerequisites

* delete redundant whitespace in links

* ignore all non-first tests

* remove redundant comments from tests

* turn time-tree into chrono-chain

* remove test_ from test names

* remove changes to concepts
  • Loading branch information
0xNeshi authored Dec 30, 2024
1 parent 42cdcfa commit 4180988
Show file tree
Hide file tree
Showing 17 changed files with 439 additions and 134 deletions.
9 changes: 5 additions & 4 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -263,14 +263,15 @@
"status": "wip"
},
{
"slug": "chrono-realms-time-tree",
"name": "Chrono Realms Time Tree",
"uuid": "a72139a1-825e-4349-a6b5-400b665fbe07",
"slug": "chrono-realms-chrono-chain",
"name": "Chrono Realms Chrono Chain",
"uuid": "e04f808a-25dc-4448-bc45-d3da0db9819a",
"concepts": [
"smart-pointers"
],
"prerequisites": [
"structs"
"traits",
"enums"
],
"status": "wip"
}
Expand Down
18 changes: 18 additions & 0 deletions exercises/concept/chrono-realms-chrono-chain/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Hints

## General

- [The Cairo Book: Smart Pointers][smart-pointers]

## 1. Build the ChronoChain from an array of `u32` values

- We can iterate through the array and construct a chain in reverse, starting from the `End` and linking each value with the `Link` variant.
- Consider using `span.pop_back()` to iterate through the array in reverse order, this will help us create the chain from the end to the start.

## 2. Sum the values in the ChronoChain

- Use pattern matching to handle the two variants of `ChronoChain`.
- When you reach `End`, the sum is 0, and when you reach `Link`, you add the value and recursively sum the rest of the chain.
- This approach is similar to a recursive function that traverses the structure until it reaches the base case (`End`).

[smart-pointers]: https://book.cairo-lang.org/ch11-02-smart-pointers.html
46 changes: 46 additions & 0 deletions exercises/concept/chrono-realms-chrono-chain/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Instructions

In **Chrono Realms**, Time Keepers often deal with not just trees of timelines, but **Chrono Chains**-sequences of linked **TimeNodes**, each representing a specific moment in time.
A **Chrono Chain** is a straight path of sequential moments, where each **TimeNode** connects to the next.
These **Chrono Chains** are useful when traveling through a series of specific events, as they allow Time Keepers to follow a single timeline.

However, to handle these potentially long **Chrono Chains**, Time Keepers use **Smart Pointers (Box<T>)** to safely manage and traverse these lists of moments without causing unnecessary memory duplication or overflow.
Each **TimeNode** holds a reference to the next node, forming a recursive structure.

Your task as an apprentice is to implement a **Chrono Chain** as a recursive list structure using smart pointers.

In this exercise, you will:

1. Create a recursive `ChronoChain` enum, representing a list of moments.
2. Use the `Box<T>` smart pointer to store the recursive nodes.
3. Implement a function to create a `ChronoChain` from an array of `u32` values.
4. Implement a function to traverse the `ChronoChain` and sum up the values stored in the list.

## 1. Define the Recursive `ChronoChain` Enum

Create a recursive enum `ChronoChain` with two variants:

- `End`: Represents the end of the list.
- `Link`: Contains a `u32` value and a boxed reference to the next node in the chain.

## 2. Create a Function to Build a ChronoChain

Write a function `ChronoChain::build` that takes an array of `u32` values and returns a `ChronoChain`, linking the values sequentially using smart pointers.

## 3. Implement the Sum Function

Write a function `ChronoChain::sum` to recursively traverse the `ChronoChain` and sum the values of all nodes.

## Example Usage

```rust
fn main() {
// Create a ChronoChain from an array of values
let chrono_chain = ChronoChain::build(array![10, 20, 30]);

// Sum the values in the ChronoChain
let total_sum = chrono_chain.sum();

println!("Total Time Power: {}", total_sum);
}
```
52 changes: 52 additions & 0 deletions exercises/concept/chrono-realms-chrono-chain/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Introduction

Smart pointers in Cairo are advanced data structures that ensure safe and efficient memory management by adding safety features to regular pointers, preventing common issues like dereferencing null pointers or accessing uninitialized memory.

## What is a Smart Pointer?

A smart pointer behaves like a regular pointer but tracks ownership and ensures safe memory access, preventing issues like null or dangling pointer dereferencing.

## Types of Smart Pointers

Cairo provides several smart pointer types, such as `Box<T>` and `Nullable<T>`:

- **`Box<T>`**: Stores data in a special memory segment, ideal for large or dynamically-sized data.
It allows transferring ownership without copying the data.
- **`Nullable<T>`**: Points to either a valid value of type `T` or `null`, useful for handling optional values.

## Memory Safety

Smart pointers help prevent unsafe memory access, ensuring memory is automatically deallocated when no longer needed, thus reducing the risk of memory leaks.

### Example: Using `Box<T>` for Recursive Types

Smart pointers like `Box<T>` allow for safe handling of recursive types, such as in a binary tree, by allocating memory efficiently and avoiding infinite recursion.

```rust
use core::box::{BoxTrait};

#[derive(Copy, Drop)]
enum BinaryTree {
Leaf: u32,
Node: (u32, Box<BinaryTree>, Box<BinaryTree>),
}

fn main() {
let leaf1 = BinaryTree::Leaf(1);
let leaf2 = BinaryTree::Leaf(2);
let node = BinaryTree::Node((3, BoxTrait::new(leaf1), BoxTrait::new(leaf2)));
println!("{:?}", node);
}
```

## Performance Benefits

Smart pointers improve performance by passing references to data instead of copying large structures, reducing memory overhead.

```rust
// `Cart` is a large struct that contains a lot of information
fn pass_pointer(cart: Box<Cart>) {
let cart = cart.unbox();
println!("{} is shopping today and bought {} items", cart.buyer, cart.items);
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"src/lib.cairo"
],
"test": [
"tests/chrono_realms_time_tree.cairo"
"tests/chrono_realms_chrono_chain.cairo"
],
"exemplar": [
".meta/exemplar.cairo"
Expand All @@ -16,5 +16,5 @@
"Scarb.toml"
]
},
"blurb": "<blurb>"
"blurb": "Learn smart pointers by building the magical Chrono Chain"
}
36 changes: 36 additions & 0 deletions exercises/concept/chrono-realms-chrono-chain/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Design

## Goal

Introduce the student to working with recursive types and smart pointers in Cairo.

## Learning objectives

- Understand how to define and use recursive types with enums in Cairo.
- Learn how to use smart pointers (`Box<T>`) for handling recursive data structures.
- Practice working with recursive functions to traverse and manipulate linked data.

## Out of scope

- Advanced memory management concepts related to smart pointers beyond `Box<T>`.
- Deep dive into optimization of recursive data structures.

## Concepts

- Enums
- Recursive types
- Smart pointers (`Box<T>`)

## Prerequisites

- Traits
- Basic understanding of enums and data types in Cairo.
- Familiarity with smart pointers in Cairo.

## Resources to refer to

- [Cairo Book - The Box Type][box]
- [Cairo Book - Enums][enums]

[box]: https://book.cairo-lang.org/ch11-02-smart-pointers.html#the-boxt-type-to-manipulate-pointers
[enums]: https://book.cairo-lang.org/ch06-01-enums.html
30 changes: 30 additions & 0 deletions exercises/concept/chrono-realms-chrono-chain/.meta/exemplar.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Define the recursive ChronoChain enum
#[derive(Copy, Drop)]
pub enum ChronoChain {
End,
Link: (u32, Box<ChronoChain>),
}

#[generate_trait]
pub impl ChronoChainImpl of ChronoChainTrait {
// Function to build a ChronoChain from an array of u32 values
fn build(arr: Array<u32>) -> ChronoChain {
let mut chain = ChronoChain::End;

// Iterate in reverse to build the chain from the end to the beginning
let mut span = arr.span();
while let Option::Some(value) = span.pop_back() {
chain = ChronoChain::Link((*value, BoxTrait::new(chain)));
};

chain
}

// Function to sum the values in the ChronoChain
fn sum(self: ChronoChain) -> u64 {
match self {
ChronoChain::End => 0,
ChronoChain::Link((value, next)) => value.into() + next.unbox().sum(),
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "chrono_realms_time_tree"
name = "chrono_realms_chrono_chain"
version = "0.1.0"
edition = "2024_07"

Expand Down
19 changes: 19 additions & 0 deletions exercises/concept/chrono-realms-chrono-chain/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Define the recursive ChronoChain enum
#[derive(Copy, Drop)]
pub enum ChronoChain {
End,
Link: (u32, Box<ChronoChain>),
}

#[generate_trait]
pub impl ChronoChainImpl of ChronoChainTrait {
// Function to build a ChronoChain from an array of u32 values
fn build(arr: Array<u32>) -> ChronoChain {
panic!("implement `ChronoChain::build`")
}

// Function to sum the values in the ChronoChain
fn sum(self: ChronoChain) -> u64 {
panic!("implement `ChronoChain::sum`")
}
}
Loading

0 comments on commit 4180988

Please sign in to comment.