Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ledger #316

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3a751ef
word search updated
Falilah Nov 3, 2024
8f154f2
word search
Falilah Nov 3, 2024
c3bce99
word search updated
Falilah Nov 3, 2024
d61cb31
word search requested changes made
Falilah Nov 6, 2024
9bba86e
word search requested changes made
Falilah Nov 6, 2024
c55068e
word search requested changes made
Falilah Nov 6, 2024
083fce0
Merge branch 'main' into main
Falilah Nov 6, 2024
6db286b
word search requested changes made
Falilah Nov 6, 2024
927c3d0
word search requested changes made
Falilah Nov 6, 2024
69f6a96
Increase difficulty
0xNeshi Nov 10, 2024
670cc19
Merge branch 'exercism:main' into main
Falilah Nov 11, 2024
93b5f38
Merge branch 'exercism:main' into main
Falilah Nov 13, 2024
21c004d
isbn verifier updated
Falilah Nov 14, 2024
a4e87b6
isbn-verifier changes updated
Falilah Nov 16, 2024
9e82734
Fix panic message
0xNeshi Nov 16, 2024
0992510
Merge branch 'exercism:main' into main
Falilah Nov 17, 2024
98c0248
Merge branch 'exercism:main' into main
Falilah Nov 30, 2024
46d42f8
vlq updated
Falilah Dec 1, 2024
056130e
vlq updated
Falilah Dec 1, 2024
71d8c35
vlq: requested changes updated
Falilah Dec 1, 2024
6b86d89
Update difficulty
0xNeshi Dec 2, 2024
acb6529
Merge branch 'exercism:main' into main
Falilah Dec 11, 2024
0498761
ledger exercise updated
Falilah Dec 12, 2024
6556f05
ledger exercise updated
Falilah Dec 12, 2024
2646017
ledger exercise updated
Falilah Dec 12, 2024
69b9c0d
ledger exercise updated
Falilah Dec 12, 2024
e37bab0
Apply some suggestions from code review
0xNeshi Dec 15, 2024
821dfbd
add missing semicolon
0xNeshi Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,14 @@
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "ledger",
"name": "Ledger",
"uuid": "cccabb0a-b5b5-4f18-aead-18f62af4a89f",
"practices": [],
"prerequisites": [],
"difficulty": 4
}
],
"foregone": [
Expand Down
4 changes: 4 additions & 0 deletions exercises/practice/ledger/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Instructions append

When working with the ledger, treat the `e` symbol as a stand-in for the Euro currency symbol (€).
This substitution ensures that the program remains functional and adheres to the ASCII-only constraint, without sacrificing usability.
14 changes: 14 additions & 0 deletions exercises/practice/ledger/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Instructions

Refactor a ledger printer.

The ledger exercise is a refactoring exercise.
There is code that prints a nicely formatted ledger, given a locale (American or Dutch) and a currency (US dollar or euro).
The code however is rather badly written, though (somewhat surprisingly) it consistently passes the test suite.

Rewrite this code.
Remember that in refactoring the trick is to make small steps that keep the tests passing.
That way you can always quickly go back to a working version.
Version control tools like git can help here as well.

Please keep a log of what changes you've made and make a comment on the exercise containing that log, this will help reviewers.
20 changes: 20 additions & 0 deletions exercises/practice/ledger/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"authors": [
"Falilah"
],
"files": {
"solution": [
"src/lib.cairo"
],
"test": [
"tests/ledger.cairo"
],
"example": [
".meta/example.cairo"
],
"invalidator": [
"Scarb.toml"
]
0xNeshi marked this conversation as resolved.
Show resolved Hide resolved
},
"blurb": "Refactor a ledger printer."
}
224 changes: 224 additions & 0 deletions exercises/practice/ledger/.meta/example.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Refactored code
use core::to_byte_array::AppendFormattedToByteArray;

#[derive(Debug, PartialEq, Drop)]
pub enum Currency {
USD,
EUR,
}

#[derive(Debug, PartialEq, Drop)]
pub enum Locale {
en_US,
nl_NL,
}

pub fn format_entries(
currency: Currency, locale: Locale, transactions: Array<(ByteArray, ByteArray, ByteArray)>
) -> Array<ByteArray> {
let mut ledger: Array<ByteArray> = array![]
0xNeshi marked this conversation as resolved.
Show resolved Hide resolved

// Step 1: Define the header based on the locale
let header = match @locale {
Locale::en_US => "Date | Description | Change ",
Locale::nl_NL => "Datum | Omschrijving | Verandering ",
};
ledger.append(header);

// Step 2: Process transactions
for (
date, transaction, change
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
date, transaction, change
date, description, amount_in_cents

as per canonical data, the names are much clearer now

) in transactions {
let formatted_date = format_date(@date, @locale);

// Format the change based on the currency and locale
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this comment above the function definition

let formatted_change = format_change(@change, @currency, @locale);

// Format the transaction row
let mut row = formatted_date;
row += " | ";
row += format_transaction(transaction);
row += " | ";
row += formatted_change;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simplify this body further:

        let date = format_date(@date, @locale);
        let change = format_change(@change, @currency, @locale);
        let row = format!("{} | {} | {}", date, format_transaction(transaction), change);
        ledger.append(row);

Additionally, you could move the body to a helper function format_row (including parameter renaming):

for transaction in transactions {
    let row = format_row(@currency, @locale, transaction);
    ledger.append(row);
};

// ...

// the inline attribute reduces gas cost a bit in this case
#[inline(always)]
fn format_row(
    currency: @Currency, locale: @Locale, transaction: (ByteArray, ByteArray, ByteArray)
) -> ByteArray {
    let (date, description, amount_in_cents) = transaction;
    let date = format_date(@date, locale);
    let amount_in_cents = format_amount(@amount_in_cents, currency, locale);
    format!("{} | {} | {}", date, format_description(description), amount_in_cents)
}


// Append the row to the ledger
ledger.append(row);
};

ledger
}

// format date based on the locale handled in 1 function
fn format_date(date: @ByteArray, locale: @Locale) -> ByteArray {
let (mut year, mut month, mut day) = split_date(date);
match locale {
Locale::en_US => {
day += "/";
month += "/";
ByteArrayTrait::concat(@month, @ByteArrayTrait::concat(@day, @year))
},
Locale::nl_NL => {
day += "-";
month += "-";
ByteArrayTrait::concat(@day, @ByteArrayTrait::concat(@month, @year))
},
}
}

// split date into year, month and day
fn split_date(date: @ByteArray) -> (ByteArray, ByteArray, ByteArray) {
let mut year = "";
let mut month = "";
let mut day = "";
let mut sep = 0;
let mut i = 0;

while i < date.len() {
Copy link
Contributor

@0xNeshi 0xNeshi Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could probably separate this single while loop into 3 loops, each building it's own part of the date.
Then you wouldn't need so many if checks within

if sep == 0 && i < 4 && date[i] != '-' {
year.append_byte(date[i]);
} else if date[i] == '-' {
sep += 1;
} else if sep == 1 && i < 7 && date[i] != '-' {
month.append_byte(date[i]);
} else {
day.append_byte(date[i]);
}
i += 1;
};

(year, month, day)
}

fn format_change(change: @ByteArray, currency: @Currency, locale: @Locale) -> ByteArray {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn format_change(change: @ByteArray, currency: @Currency, locale: @Locale) -> ByteArray {
fn format_amount(amount_in_cents: @ByteArray, currency: @Currency, locale: @Locale) -> ByteArray {

let mut int_value: u32 = 0;
let mut negative = false;
let mut i = 0;

if change[i] == '-' {
negative = true;
i += 1;
}

while i < change.len() {
if let Option::Some(digit) = char_to_digit(change[i]) {
int_value = int_value * 10 + digit.into();
}
i += 1;
};

let formatted_value = format_number(int_value, negative, currency, locale);
let mut extra = "";

if formatted_value.len() < 13 {
let diff = 13 - formatted_value.len();
let mut i = 0;
while i < diff {
extra += " ";
i += 1;
}
}
ByteArrayTrait::concat(@extra, @formatted_value)
}

fn format_number(value: u32, negative: bool, currency: @Currency, locale: @Locale) -> ByteArray {
let mut result = "";

if negative && locale == @Locale::en_US {
result.append_byte('(');
}

match currency {
Currency::USD => result.append_byte('$'),
Currency::EUR => result.append_byte('e'),
};

if locale == @Locale::nl_NL {
result.append_byte(' ');
}

if negative && locale != @Locale::en_US {
result.append_byte('-');
}
Comment on lines +135 to +141
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if locale == @Locale::nl_NL {
result.append_byte(' ');
}
if negative && locale != @Locale::en_US {
result.append_byte('-');
}
if locale == @Locale::nl_NL {
result.append_byte(' ');
if negative {
result.append_byte('-');
}
}

Because: locale != @Locale::en_US is the same as locale == @Locale::nl_NL


let whole = value / 100;

result += add_sep(whole, locale);
let fraction = value % 100;
if locale == @Locale::en_US {
result.append_byte('.');
} else {
result.append_byte(',');
}

if fraction < 10 {
result.append_byte('0');
@fraction.append_formatted_to_byte_array(ref result, 10);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@fraction.append_formatted_to_byte_array(ref result, 10);
fraction.append_formatted_to_byte_array(ref result, 10);

you don't need the snapshot @, it gets automatically added within the function call, you can remove it everywhere

} else {
@fraction.append_formatted_to_byte_array(ref result, 10);
}

if negative && locale == @Locale::en_US {
result.append_byte(')');
} else {
result.append_byte(' ');
}

result
}


fn add_sep(whole: u32, locale: @Locale) -> ByteArray {
let mut result = "";
let mut temp = "";
@whole.append_formatted_to_byte_array(ref temp, 10);
if temp.len() > 3 {
result.append_byte(temp[0]);
let mut i = 1;
let mut sep = 0;
while i < temp.len() {
if sep == 0 {
if locale == @Locale::nl_NL {
result.append_byte('.');
} else {
result.append_byte(',');
}
sep = 3;
}
result.append_byte(temp[i]);
i += 1;
sep -= 1;
}
} else {
result += temp;
}
result
}

fn format_transaction(transaction: ByteArray) -> ByteArray {
let mut formatted = "";

if transaction.len() > 22 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does 22 mean? Use a const variable with a meaningful name

let mut i = 0;
while i < 22 {
formatted.append_byte(transaction[i]);
i += 1;
};
formatted += "...";
} else {
formatted += transaction;
while formatted.len() < 25 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does 25 mean? Use a const variable with a meaningful name

formatted.append_byte(' ');
};
}

formatted
}


fn char_to_digit(c: u8) -> Option<u8> {
if c >= '0' && c <= '9' {
Option::Some(c - '0')
} else {
Option::None
}
}
48 changes: 48 additions & 0 deletions exercises/practice/ledger/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[d131ecae-a30e-436c-b8f3-858039a27234]
description = "empty ledger"

[ce4618d2-9379-4eca-b207-9df1c4ec8aaa]
description = "one entry"

[8d02e9cb-e6ee-4b77-9ce4-e5aec8eb5ccb]
description = "credit and debit"

[502c4106-0371-4e7c-a7d8-9ce33f16ccb1]
description = "multiple entries on same date ordered by description"
include = false

[29dd3659-6c2d-4380-94a8-6d96086e28e1]
description = "final order tie breaker is change"

[9b9712a6-f779-4f5c-a759-af65615fcbb9]
description = "overlong description is truncated"

[67318aad-af53-4f3d-aa19-1293b4d4c924]
description = "euros"

[bdc499b6-51f5-4117-95f2-43cb6737208e]
description = "Dutch locale"

[86591cd4-1379-4208-ae54-0ee2652b4670]
description = "Dutch locale and euros"

[876bcec8-d7d7-4ba4-82bd-b836ac87c5d2]
description = "Dutch negative number with 3 digits before decimal point"

[29670d1c-56be-492a-9c5e-427e4b766309]
description = "American negative number with 3 digits before decimal point"

[9c70709f-cbbd-4b3b-b367-81d7c6101de4]
description = "multiple entries on same date ordered by description"
reimplements = "502c4106-0371-4e7c-a7d8-9ce33f16ccb1"
7 changes: 7 additions & 0 deletions exercises/practice/ledger/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "ledger"
version = "0.1.0"
edition = "2024_07"

[dev-dependencies]
cairo_test = "2.9.2"
Loading
Loading