From ec2b4080b485a4504f30accd97a47ff3ff0ff06e Mon Sep 17 00:00:00 2001 From: faliah Date: Tue, 24 Dec 2024 14:17:44 +0100 Subject: [PATCH 01/11] ledger: updated --- config.json | 8 + .../ledger/.docs/instructions.append.md | 4 + .../practice/ledger/.docs/instructions.md | 14 + exercises/practice/ledger/.meta/config.json | 20 + exercises/practice/ledger/.meta/example.cairo | 257 ++++++++++++ exercises/practice/ledger/.meta/tests.toml | 48 +++ exercises/practice/ledger/Scarb.toml | 7 + exercises/practice/ledger/src/lib.cairo | 376 ++++++++++++++++++ exercises/practice/ledger/tests/ledger.cairo | 210 ++++++++++ 9 files changed, 944 insertions(+) create mode 100644 exercises/practice/ledger/.docs/instructions.append.md create mode 100644 exercises/practice/ledger/.docs/instructions.md create mode 100644 exercises/practice/ledger/.meta/config.json create mode 100644 exercises/practice/ledger/.meta/example.cairo create mode 100644 exercises/practice/ledger/.meta/tests.toml create mode 100644 exercises/practice/ledger/Scarb.toml create mode 100644 exercises/practice/ledger/src/lib.cairo create mode 100644 exercises/practice/ledger/tests/ledger.cairo diff --git a/config.json b/config.json index 574ec5e8..e691f6fc 100644 --- a/config.json +++ b/config.json @@ -892,6 +892,14 @@ "practices": [], "prerequisites": [], "difficulty": 3 + }, + { + "slug": "ledger", + "name": "Ledger", + "uuid": "fb9758db-359a-4839-b37e-7a462906846a", + "practices": [], + "prerequisites": [], + "difficulty": 1 } ], "foregone": [ diff --git a/exercises/practice/ledger/.docs/instructions.append.md b/exercises/practice/ledger/.docs/instructions.append.md new file mode 100644 index 00000000..97c9e71f --- /dev/null +++ b/exercises/practice/ledger/.docs/instructions.append.md @@ -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. diff --git a/exercises/practice/ledger/.docs/instructions.md b/exercises/practice/ledger/.docs/instructions.md new file mode 100644 index 00000000..a53e5c15 --- /dev/null +++ b/exercises/practice/ledger/.docs/instructions.md @@ -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. diff --git a/exercises/practice/ledger/.meta/config.json b/exercises/practice/ledger/.meta/config.json new file mode 100644 index 00000000..fa548014 --- /dev/null +++ b/exercises/practice/ledger/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "Falilah" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/ledger.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "blurb": "Refactor a ledger printer." +} diff --git a/exercises/practice/ledger/.meta/example.cairo b/exercises/practice/ledger/.meta/example.cairo new file mode 100644 index 00000000..3cf6c738 --- /dev/null +++ b/exercises/practice/ledger/.meta/example.cairo @@ -0,0 +1,257 @@ +/* Changes in the Refactored Code: + +1) format_entries function now uses format_row to create each ledger entry, reducing redundancy. + +2) format_row function to format each row, combining date, description, and amount formatting into a single string. + +3) format_date Refactor split date formatting into a standalone function that uses split_date for modular handling. + +4) split_date Function introduced to extract year, month, and day components, improving clarity and code reuse. + +5) format_amount Improvements refactored logic for formatting amounts to make negative handling, spacing, and locale-specific formatting cleaner. + +6) format_number refinement Handles separators and decimal points for numbers more systematically. + +7) Enhanced add_sep Split logic into separate steps for readability, making prefix, middle, and final result assembly explicit. + +8) Revised format_description logic adjusted to use constants for truncation and padding, aligning with coding standards. + +9) Added AMOUNT_COLUMN_WIDTH, MAX_DESCRIPTION_LENGTH and FORMATTED_DESCRIPTION_LENGTH constants for readability and maintainability. + +10) Imported AppendFormattedToByteArray to handle the conversion of byteArray to integer.s + + + Overall, the refactored code is modular, better adheres to single-responsibility principles, and improves readability while ensuring maintainability. + */ +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, +} + +#[derive(Debug, PartialEq, Drop)] +pub struct Entry { + date: ByteArray, + description: ByteArray, + amount_in_cents: i32, +} + + + + +pub fn format_entries( + currency: Currency, locale: Locale, entries: Array +) -> Array { + let mut ledger: Array = array![]; + + // 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 entry in entries { + let row = format_row(@currency, @locale, entry); + ledger.append(row); + }; + + ledger +} + +fn format_row(currency: @Currency, locale: @Locale, entry: Entry) -> ByteArray { + let date = format_date(@entry.date, locale); + let amount_in_cents = format_amount(@entry.amount_in_cents, currency, locale); + format!("{} | {} | {}", date, format_description(entry.description), amount_in_cents) +} + +// format date based on the locale +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() { + 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_amount(amount_in_cents: @i32, currency: @Currency, locale: @Locale) -> ByteArray { + let amount_in_cents = format!("{amount_in_cents}"); + let mut int_value: u32 = 0; + let mut negative = false; + let mut i = 0; + + if amount_in_cents[i] == '-' { + negative = true; + i += 1; + } + + const AMOUNT_COLUMN_WIDTH: usize = 13; + while i < amount_in_cents.len() { + let zero_ascii = '0'; + if let Option::Some(digit) = Option::Some(amount_in_cents[i] - zero_ascii) { + 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() < AMOUNT_COLUMN_WIDTH { + let diff = AMOUNT_COLUMN_WIDTH - 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 { + result.append_byte('-'); + } + } + + + 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); + } 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); + + // Step 1: Append the first character if length > 3 + let mut prefix = ""; + if temp.len() > 3 { + prefix.append_byte(temp[0]); + } else { + result += temp; // If length <= 3, the result is directly temp + return result; + } + + // Step 2: Build the middle part with separators + let mut middle = ""; + let mut i = 1; + let mut sep = 0; + while i < temp.len() { + if sep == 0 { + if locale == @Locale::nl_NL { + middle.append_byte('.'); + } else { + middle.append_byte(','); + } + sep = 3; + } + middle.append_byte(temp[i]); + i += 1; + sep -= 1; + }; + + // Step 3: Combine prefix and middle + result = ByteArrayTrait::concat(@prefix, @middle); + + result +} + +fn format_description(transaction: ByteArray) -> ByteArray { + let mut formatted = ""; + const MAX_DESCRIPTION_LENGTH: usize = 22; + const FORMATTED_DESCRIPTION_LENGTH: usize = 25; + + if transaction.len() > MAX_DESCRIPTION_LENGTH { + let mut i = 0; + while i < MAX_DESCRIPTION_LENGTH { + formatted.append_byte(transaction[i]); + i += 1; + }; + formatted += "..."; + } else { + formatted += transaction; + while formatted.len() < FORMATTED_DESCRIPTION_LENGTH { + formatted.append_byte(' '); + }; + } + + formatted +} diff --git a/exercises/practice/ledger/.meta/tests.toml b/exercises/practice/ledger/.meta/tests.toml new file mode 100644 index 00000000..4ea45ceb --- /dev/null +++ b/exercises/practice/ledger/.meta/tests.toml @@ -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" diff --git a/exercises/practice/ledger/Scarb.toml b/exercises/practice/ledger/Scarb.toml new file mode 100644 index 00000000..78db418a --- /dev/null +++ b/exercises/practice/ledger/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "ledger" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.9.2" diff --git a/exercises/practice/ledger/src/lib.cairo b/exercises/practice/ledger/src/lib.cairo new file mode 100644 index 00000000..93df9446 --- /dev/null +++ b/exercises/practice/ledger/src/lib.cairo @@ -0,0 +1,376 @@ +#[derive(Debug, PartialEq, Drop)] +pub enum Currency { + USD, + EUR, + JPY, +} + +#[derive(Debug, PartialEq, Drop)] +pub enum Locale { + en_US, + nl_NL, +} + +#[derive(Debug, PartialEq, Drop)] +pub struct Entry { + pub date: ByteArray, + pub description: ByteArray, + pub amount_in_cents: i32, +} + +pub fn format_entries( + currency: Currency, locale: Locale, entries: Array, +) -> Array { + let mut ledger: Array = ArrayTrait::new(); + let mut header = ""; + if locale == Locale::en_US { + header = "Date | Description | Change "; + } else if locale == Locale::nl_NL { + header = "Datum | Omschrijving | Verandering "; + } + // date, transaction, change + ledger.append(header); + for entry in entries { + let mut formatted_date = ""; + if locale == Locale::en_US { + let mut year = "/"; + let mut month = ""; + let mut day = "/"; + + let mut i = 0; + let mut sep = 0; + + while i < entry.date.len() { + if sep == 0 && i < 4 && entry.date[i] != '-' { + year.append_byte(entry.date[i]); + } else if entry.date[i] == '-' { + sep += 1; + } else if sep == 1 && i < 7 && entry.date[i] != '-' { + month.append_byte(entry.date[i]); + } else { + day.append_byte(entry.date[i]); + } + i += 1; + }; + + formatted_date = ByteArrayTrait::concat(@month, @ByteArrayTrait::concat(@day, @year)) + } else { + let mut year = "-"; + let mut month = "-"; + let mut day = ""; + + let mut i = 0; + let mut sep = 0; + + while i < entry.date.len() { + if sep == 0 && i < 4 && entry.date[i] != '-' { + year.append_byte(entry.date[i]); + } else if entry.date[i] == '-' { + sep += 1; + } else if sep == 1 && i < 7 && entry.date[i] != '-' { + month.append_byte(entry.date[i]); + } else { + day.append_byte(entry.date[i]); + } + i += 1; + }; + + formatted_date = ByteArrayTrait::concat(@day, @ByteArrayTrait::concat(@month, @year)); + } + + let mut formatted_change = ""; + let amount_in_cents = entry.amount_in_cents; + let change = format!("{amount_in_cents}"); + if currency == Currency::USD { + if locale == Locale::en_US { + let mut result = "$"; + let mut i = 0; + let mut op = ""; + let mut cl = ""; + if change[i] == '-' { + op += "("; + cl += ")"; + i += 1; + } else { + op += " "; + cl += " "; + } + let mut int: u32 = 0; + while i < change.len() { + let zero_ascii = '0'; + let c = Option::Some(change[i] - zero_ascii); + + match c { + Option::Some(v) => { int = int * 10 + v.into(); }, + Option::None => { break; }, + } + i += 1; + }; + + let val = int / 100; + let mut temp = format!("{val}"); + if temp.len() > 3 { + result.append_byte(temp[0]); + let mut i = 1; + let mut sep = 0; + while i < temp.len() { + if sep == 0 { + result.append_byte(','); + sep = 3; + } + result.append_byte(temp[i]); + i += 1; + sep -= 1; + } + } else { + result += temp; + } + + result += "."; + let mut rem = int % 100; + if int < 10 { + result += "0"; + } + + let rem = format!("{rem}"); + result += rem; + if result[result.len() - 2] == '.' { + result += "0"; + } + + result = ByteArrayTrait::concat(@ByteArrayTrait::concat(@op, @result), @cl); + + let mut extra = ""; + if result.len() < 13 { + let diff = 13 - result.len(); + let mut i = 0; + while i < diff { + extra += " "; + i += 1; + } + } + + formatted_change = ByteArrayTrait::concat(@extra, @result); + } else { + let mut result = " $ "; + let mut i = 0; + let mut op = ""; + let mut cl = ""; + if change[i] == '-' { + result = " $ -"; + i += 1; + } + op += " "; + cl += " "; + + let mut int: u32 = 0; + while i < change.len() { + let zero_ascii = '0'; + let c = Option::Some(change[i] - zero_ascii); + + match c { + Option::Some(v) => { int = int * 10 + v.into(); }, + Option::None => { break; }, + } + i += 1; + }; + + let val = int / 100; + let mut temp = format!("{val}"); + if temp.len() > 3 { + result.append_byte(temp[0]); + let mut i = 1; + let mut sep = 0; + while i < temp.len() { + if sep == 0 { + result.append_byte('.'); + sep = 3; + } + result.append_byte(temp[i]); + i += 1; + sep -= 1; + } + } else { + result += temp; + } + + result += ","; + let mut rem = int % 100; + if int < 10 { + result += "0"; + } + + let rem = format!("{rem}"); + result += rem; + if result[result.len() - 2] == ',' { + result += "0"; + } + + result = ByteArrayTrait::concat(@ByteArrayTrait::concat(@op, @result), @cl); + + let mut extra = ""; + if result.len() < 13 { + let diff = 13 - result.len(); + let mut i = 0; + while i < diff { + extra += " "; + i += 1; + } + } + + formatted_change = ByteArrayTrait::concat(@extra, @result); + } + } else { + if locale == Locale::en_US { + // formatted_change = format_eur(change); + + let mut result = "e"; + let mut i = 0; + let mut op = ""; + let mut cl = ""; + if change[i] == '-' { + op += "("; + cl += ")"; + i += 1; + } else { + op += " "; + cl += " "; + } + let mut int: u32 = 0; + while i < change.len() { + let zero_ascii = '0'; + let c = Option::Some(change[i] - zero_ascii); + + match c { + Option::Some(v) => { int = int * 10 + v.into(); }, + Option::None => { break; }, + } + i += 1; + }; + + let val = int / 100; + let val = format!("{val}"); + result += val; + result += "."; + let rem = int % 100; + if int < 10 { + result += "0"; + } + let mut rem = format!("{rem}"); + if rem.len() < 2 { + rem += "0"; + } + result += rem; + + result = ByteArrayTrait::concat(@ByteArrayTrait::concat(@op, @result), @cl); + + let mut extra = ""; + if result.len() < 13 { + let diff = 13 - result.len(); + let mut i = 0; + while i < diff { + extra += " "; + i += 1; + } + } + + formatted_change = ByteArrayTrait::concat(@extra, @result); + } else { + // formatted_change = format_eur_nil(change); + + let mut result = "e "; + let mut i = 0; + let mut op = ""; + let mut cl = ""; + if change[i] == '-' { + op += "("; + cl += ")"; + i += 1; + } else { + op += " "; + cl += " "; + } + let mut int: u32 = 0; + while i < change.len() { + let zero_ascii = '0'; + let c = Option::Some(change[i] - zero_ascii); + + match c { + Option::Some(v) => { int = int * 10 + v.into(); }, + Option::None => { break; }, + } + i += 1; + }; + let val = int / 100; + let mut temp = format!("{val}"); + if temp.len() > 3 { + result.append_byte(temp[0]); + let mut i = 1; + let mut sep = 0; + while i < temp.len() { + if sep == 0 { + result.append_byte('.'); + sep = 3; + } + result.append_byte(temp[i]); + i += 1; + sep -= 1; + } + } else { + result += temp; + } + result += ","; + let rem = int % 100; + if int < 10 { + result += "0"; + } + let mut rem = format!("{rem}"); + if rem.len() < 2 && int >= 10 { + rem += "0"; + } + result += rem; + + result = ByteArrayTrait::concat(@ByteArrayTrait::concat(@op, @result), @cl); + let mut extra = ""; + if result.len() < 13 { + let diff = 13 - result.len(); + let mut i = 0; + while i < diff { + extra += " "; + i += 1; + } + } + + formatted_change = ByteArrayTrait::concat(@extra, @result); + } + } + + let mut row = formatted_date; + row += " | "; + if entry.description.len() > 22 { + let mut i = 0; + while i < 22 { + row.append_byte(entry.description[i]); + i += 1; + }; + row += "..."; + } else { + row += entry.description; + } + + if row.len() < 38 { + let dif = 38 - row.len(); + let mut i = 0; + while i < dif { + row += " "; + i += 1; + } + } + row += " | "; + row += formatted_change; + + ledger.append(row); + }; + + ledger +} diff --git a/exercises/practice/ledger/tests/ledger.cairo b/exercises/practice/ledger/tests/ledger.cairo new file mode 100644 index 00000000..2635c133 --- /dev/null +++ b/exercises/practice/ledger/tests/ledger.cairo @@ -0,0 +1,210 @@ +use ledger::{format_entries, Currency, Locale, Entry}; + +#[test] +fn empty_ledger() { + let currency = Currency::USD; + let locale = Locale::en_US; + let entries = array![]; + + let expected: Array = array![ + "Date | Description | Change ", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + +#[test] +#[ignore] +fn one_entry() { + let currency = Currency::USD; + let locale = Locale::en_US; + let entries = array![ + Entry { date: "2015-01-01", description: "Buy present", amount_in_cents: -1000 }, + ]; + + let expected: Array = array![ + "Date | Description | Change ", + "01/01/2015 | Buy present | ($10.00)", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + +#[test] +#[ignore] +fn credit_and_debit() { + let currency = Currency::USD; + let locale = Locale::en_US; + let entries = array![ + Entry { date: "2015-01-01", description: "Buy present", amount_in_cents: -1000 }, + Entry { date: "2015-01-02", description: "Get present", amount_in_cents: 1000 }, + ]; + + let expected: Array = array![ + "Date | Description | Change ", + "01/01/2015 | Buy present | ($10.00)", + "01/02/2015 | Get present | $10.00 ", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + +#[test] +#[ignore] +fn final_order_tie_breaker_is_change() { + let currency = Currency::USD; + let locale = Locale::en_US; + + let entries = array![ + Entry { date: "2015-01-01", description: "Something", amount_in_cents: -1 }, + Entry { date: "2015-01-01", description: "Something", amount_in_cents: 0 }, + Entry { date: "2015-01-01", description: "Something", amount_in_cents: 1 }, + ]; + + let expected: Array = array![ + "Date | Description | Change ", + "01/01/2015 | Something | ($0.01)", + "01/01/2015 | Something | $0.00 ", + "01/01/2015 | Something | $0.01 ", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + + +#[test] +#[ignore] +fn overlong_description_is_truncated() { + let currency = Currency::USD; + let locale = Locale::en_US; + let entries = array![ + Entry { + date: "2015-01-01", + description: "Freude schoner Gotterfunken", + amount_in_cents: -123456, + }, + ]; + + let expected: Array = array![ + "Date | Description | Change ", + "01/01/2015 | Freude schoner Gotterf... | ($1,234.56)", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + +#[test] +#[ignore] +fn euros() { + let currency = Currency::EUR; + let locale = Locale::en_US; + let entries = array![ + Entry { date: "2015-01-01", description: "Buy present", amount_in_cents: -1000 }, + ]; + + let expected: Array = array![ + "Date | Description | Change ", + "01/01/2015 | Buy present | (e10.00)", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + +#[test] +#[ignore] +fn dutch_locale() { + let currency = Currency::USD; + let locale = Locale::nl_NL; + let entries = array![ + Entry { date: "2015-03-12", description: "Buy present", amount_in_cents: 123456 }, + ]; + + let expected: Array = array![ + "Datum | Omschrijving | Verandering ", + "12-03-2015 | Buy present | $ 1.234,56 ", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + +#[test] +#[ignore] +fn dutch_locale_and_euros() { + let currency = Currency::EUR; + let locale = Locale::nl_NL; + let entries = array![ + Entry { date: "2015-03-12", description: "Buy present", amount_in_cents: 123456 }, + ]; + let expected: Array = array![ + "Datum | Omschrijving | Verandering ", + "12-03-2015 | Buy present | e 1.234,56 ", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + + +#[test] +#[ignore] +fn dutch_negative_number_with_3_digits_before_decimal_point() { + let currency = Currency::USD; + let locale = Locale::nl_NL; + let entries = array![ + Entry { date: "2015-03-12", description: "Buy present", amount_in_cents: -12345 }, + ]; + + let expected: Array = array![ + "Datum | Omschrijving | Verandering ", + "12-03-2015 | Buy present | $ -123,45 ", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + +#[test] +#[ignore] +fn american_negative_number_with_3_digits_before_decimal_point() { + let currency = Currency::USD; + let locale = Locale::en_US; + let entries = array![ + Entry { date: "2015-03-12", description: "Buy present", amount_in_cents: -12345 }, + ]; + + let expected: Array = array![ + "Date | Description | Change ", + "03/12/2015 | Buy present | ($123.45)", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} + + +#[test] +#[ignore] +fn multiple_entries_on_same_date_ordered_by_description() { + let currency = Currency::USD; + let locale = Locale::en_US; + let entries = array![ + Entry { date: "2015-01-01", description: "Buy present", amount_in_cents: -1000 }, + Entry { date: "2015-01-01", description: "Get present", amount_in_cents: 1000 }, + ]; + + let expected: Array = array![ + "Date | Description | Change ", + "01/01/2015 | Buy present | ($10.00)", + "01/01/2015 | Get present | $10.00 ", + ]; + let result = format_entries(currency, locale, entries); + + assert_eq!(result, expected); +} From ff243a69e1ce00f0fbaf97aa500342fed81e690c Mon Sep 17 00:00:00 2001 From: faliah Date: Tue, 24 Dec 2024 14:23:16 +0100 Subject: [PATCH 02/11] ledger: updated --- exercises/practice/ledger/.meta/example.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/practice/ledger/.meta/example.cairo b/exercises/practice/ledger/.meta/example.cairo index 3cf6c738..48a927c7 100644 --- a/exercises/practice/ledger/.meta/example.cairo +++ b/exercises/practice/ledger/.meta/example.cairo @@ -1,4 +1,4 @@ -/* Changes in the Refactored Code: +/* Changes in the Refactored Code 1) format_entries function now uses format_row to create each ledger entry, reducing redundancy. @@ -14,7 +14,7 @@ 7) Enhanced add_sep Split logic into separate steps for readability, making prefix, middle, and final result assembly explicit. -8) Revised format_description logic adjusted to use constants for truncation and padding, aligning with coding standards. +8)format_description logic for truncation and padding, aligning with coding standards. 9) Added AMOUNT_COLUMN_WIDTH, MAX_DESCRIPTION_LENGTH and FORMATTED_DESCRIPTION_LENGTH constants for readability and maintainability. From c7b41164a7d52a8494c565efd6cb172e3ef7a4a2 Mon Sep 17 00:00:00 2001 From: faliah Date: Tue, 24 Dec 2024 14:25:34 +0100 Subject: [PATCH 03/11] ledger: updated --- exercises/practice/ledger/.meta/example.cairo | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/exercises/practice/ledger/.meta/example.cairo b/exercises/practice/ledger/.meta/example.cairo index 48a927c7..62c8f57e 100644 --- a/exercises/practice/ledger/.meta/example.cairo +++ b/exercises/practice/ledger/.meta/example.cairo @@ -1,28 +1,28 @@ -/* Changes in the Refactored Code +// Changes in the Refactored Code -1) format_entries function now uses format_row to create each ledger entry, reducing redundancy. +// 1) format_entries function now uses format_row to create each ledger entry, reducing redundancy. -2) format_row function to format each row, combining date, description, and amount formatting into a single string. +// 2) format_row function to format each row, combining date, description, and amount formatting into a single string. -3) format_date Refactor split date formatting into a standalone function that uses split_date for modular handling. +// 3) format_date Refactor split date formatting into a standalone function that uses split_date for modular handling. -4) split_date Function introduced to extract year, month, and day components, improving clarity and code reuse. +// 4) split_date Function introduced to extract year, month, and day components, improving clarity and code reuse. -5) format_amount Improvements refactored logic for formatting amounts to make negative handling, spacing, and locale-specific formatting cleaner. +// 5) format_amount Improvements refactored logic for formatting amounts to make negative handling, spacing, and locale-specific formatting cleaner. -6) format_number refinement Handles separators and decimal points for numbers more systematically. +// 6) format_number refinement Handles separators and decimal points for numbers more systematically. -7) Enhanced add_sep Split logic into separate steps for readability, making prefix, middle, and final result assembly explicit. +// 7) Enhanced add_sep Split logic into separate steps for readability, making prefix, middle, and final result assembly explicit. -8)format_description logic for truncation and padding, aligning with coding standards. +// 8)format_description logic for truncation and padding, aligning with coding standards. -9) Added AMOUNT_COLUMN_WIDTH, MAX_DESCRIPTION_LENGTH and FORMATTED_DESCRIPTION_LENGTH constants for readability and maintainability. +// 9) Added AMOUNT_COLUMN_WIDTH, MAX_DESCRIPTION_LENGTH and FORMATTED_DESCRIPTION_LENGTH constants for readability and maintainability. -10) Imported AppendFormattedToByteArray to handle the conversion of byteArray to integer.s +// 10) Imported AppendFormattedToByteArray to handle the conversion of byteArray to integer.s - Overall, the refactored code is modular, better adheres to single-responsibility principles, and improves readability while ensuring maintainability. - */ +// Overall, the refactored code is modular, better adheres to single-responsibility principles, and improves readability while ensuring maintainability. + use core::to_byte_array::AppendFormattedToByteArray; #[derive(Debug, PartialEq, Drop)] From c89764ca88d076c63d3b674531c574ea3bdb2e5e Mon Sep 17 00:00:00 2001 From: faliah Date: Tue, 24 Dec 2024 14:35:09 +0100 Subject: [PATCH 04/11] ledger: updated --- exercises/practice/ledger/.meta/example.cairo | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/exercises/practice/ledger/.meta/example.cairo b/exercises/practice/ledger/.meta/example.cairo index 62c8f57e..ccbcfc0c 100644 --- a/exercises/practice/ledger/.meta/example.cairo +++ b/exercises/practice/ledger/.meta/example.cairo @@ -2,26 +2,34 @@ // 1) format_entries function now uses format_row to create each ledger entry, reducing redundancy. -// 2) format_row function to format each row, combining date, description, and amount formatting into a single string. +// 2) format_row function to format each row, combining date, description, and amount formatting into a +// single string. -// 3) format_date Refactor split date formatting into a standalone function that uses split_date for modular handling. +// 3) format_date Refactor split date formatting into a standalone function that uses split_date for +// modular handling. -// 4) split_date Function introduced to extract year, month, and day components, improving clarity and code reuse. +// 4) split_date Function introduced to extract year, month, and day components, improving clarity +// and code reuse. -// 5) format_amount Improvements refactored logic for formatting amounts to make negative handling, spacing, and locale-specific formatting cleaner. +// 5) format_amount Improvements refactored logic for formatting amounts to make negative handling, +// spacing, and locale-specific formatting cleaner. -// 6) format_number refinement Handles separators and decimal points for numbers more systematically. +// 6) format_number refinement Handles separators and decimal points for numbers more +// systematically. -// 7) Enhanced add_sep Split logic into separate steps for readability, making prefix, middle, and final result assembly explicit. +// 7) Enhanced add_sep Split logic into separate steps for readability, making prefix, middle, and +// final result assembly explicit. // 8)format_description logic for truncation and padding, aligning with coding standards. -// 9) Added AMOUNT_COLUMN_WIDTH, MAX_DESCRIPTION_LENGTH and FORMATTED_DESCRIPTION_LENGTH constants for readability and maintainability. +// 9) Added AMOUNT_COLUMN_WIDTH, MAX_DESCRIPTION_LENGTH and FORMATTED_DESCRIPTION_LENGTH constants +// for readability and maintainability. -// 10) Imported AppendFormattedToByteArray to handle the conversion of byteArray to integer.s +// 10) Imported AppendFormattedToByteArray to handle the conversion of byteArray to integer. -// Overall, the refactored code is modular, better adheres to single-responsibility principles, and improves readability while ensuring maintainability. +// Overall, the refactored code is modular, better adheres to single-responsibility principles, and +// improves readability while ensuring maintainability. use core::to_byte_array::AppendFormattedToByteArray; @@ -45,10 +53,8 @@ pub struct Entry { } - - pub fn format_entries( - currency: Currency, locale: Locale, entries: Array + currency: Currency, locale: Locale, entries: Array, ) -> Array { let mut ledger: Array = array![]; @@ -168,7 +174,6 @@ fn format_number(value: u32, negative: bool, currency: @Currency, locale: @Local } } - let whole = value / 100; result += add_sep(whole, locale); From 41b868033f5d4fa154c00ea731b58ba912556f62 Mon Sep 17 00:00:00 2001 From: faliah Date: Tue, 24 Dec 2024 14:47:29 +0100 Subject: [PATCH 05/11] ledger: updated --- exercises/practice/ledger/.meta/example.cairo | 6 +++--- exercises/practice/ledger/tests/ledger.cairo | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/practice/ledger/.meta/example.cairo b/exercises/practice/ledger/.meta/example.cairo index ccbcfc0c..5c5d1504 100644 --- a/exercises/practice/ledger/.meta/example.cairo +++ b/exercises/practice/ledger/.meta/example.cairo @@ -47,9 +47,9 @@ pub enum Locale { #[derive(Debug, PartialEq, Drop)] pub struct Entry { - date: ByteArray, - description: ByteArray, - amount_in_cents: i32, + pub date: ByteArray, + pub description: ByteArray, + pub amount_in_cents: i32, } diff --git a/exercises/practice/ledger/tests/ledger.cairo b/exercises/practice/ledger/tests/ledger.cairo index 2635c133..865cfeb7 100644 --- a/exercises/practice/ledger/tests/ledger.cairo +++ b/exercises/practice/ledger/tests/ledger.cairo @@ -1,4 +1,4 @@ -use ledger::{format_entries, Currency, Locale, Entry}; +use ledger::{format_entries, Currency, Locale, Entry, date, description, amount_in_cents}; #[test] fn empty_ledger() { From 92980a9ccbe49e466ab7ee3f758ad5d3c5a7bcd4 Mon Sep 17 00:00:00 2001 From: faliah Date: Tue, 24 Dec 2024 14:51:19 +0100 Subject: [PATCH 06/11] ledger: updated --- exercises/practice/ledger/.meta/example.cairo | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/exercises/practice/ledger/.meta/example.cairo b/exercises/practice/ledger/.meta/example.cairo index 5c5d1504..0acdf531 100644 --- a/exercises/practice/ledger/.meta/example.cairo +++ b/exercises/practice/ledger/.meta/example.cairo @@ -2,8 +2,8 @@ // 1) format_entries function now uses format_row to create each ledger entry, reducing redundancy. -// 2) format_row function to format each row, combining date, description, and amount formatting into a -// single string. +// 2) format_row function to format each row, combining date, description, and amount formatting +// into a single string. // 3) format_date Refactor split date formatting into a standalone function that uses split_date for // modular handling. @@ -30,7 +30,6 @@ // Overall, the refactored code is modular, better adheres to single-responsibility principles, and // improves readability while ensuring maintainability. - use core::to_byte_array::AppendFormattedToByteArray; #[derive(Debug, PartialEq, Drop)] From 094534b4cfe109370ebadb2952fc6dd56b7597ac Mon Sep 17 00:00:00 2001 From: faliah Date: Tue, 24 Dec 2024 14:56:44 +0100 Subject: [PATCH 07/11] ledger: updated --- exercises/practice/ledger/.meta/example.cairo | 1 - 1 file changed, 1 deletion(-) diff --git a/exercises/practice/ledger/.meta/example.cairo b/exercises/practice/ledger/.meta/example.cairo index 0acdf531..6658f705 100644 --- a/exercises/practice/ledger/.meta/example.cairo +++ b/exercises/practice/ledger/.meta/example.cairo @@ -27,7 +27,6 @@ // 10) Imported AppendFormattedToByteArray to handle the conversion of byteArray to integer. - // Overall, the refactored code is modular, better adheres to single-responsibility principles, and // improves readability while ensuring maintainability. use core::to_byte_array::AppendFormattedToByteArray; From 0a1333d4cc42b46e98f58845aa37b762e1314a01 Mon Sep 17 00:00:00 2001 From: faliah Date: Tue, 24 Dec 2024 15:01:08 +0100 Subject: [PATCH 08/11] ledger: updated --- exercises/practice/ledger/.meta/example.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/practice/ledger/.meta/example.cairo b/exercises/practice/ledger/.meta/example.cairo index 6658f705..0987996d 100644 --- a/exercises/practice/ledger/.meta/example.cairo +++ b/exercises/practice/ledger/.meta/example.cairo @@ -27,8 +27,8 @@ // 10) Imported AppendFormattedToByteArray to handle the conversion of byteArray to integer. -// Overall, the refactored code is modular, better adheres to single-responsibility principles, and -// improves readability while ensuring maintainability. +// Overall, the refactored code is modular, better adheres to single-responsibility principles, +// and improves readability while ensuring maintainability. use core::to_byte_array::AppendFormattedToByteArray; #[derive(Debug, PartialEq, Drop)] From c09f02bc53f1358dafb4700d705ce344ddf3b000 Mon Sep 17 00:00:00 2001 From: faliah Date: Tue, 24 Dec 2024 15:21:43 +0100 Subject: [PATCH 09/11] ledger: updated --- exercises/practice/ledger/tests/ledger.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/ledger/tests/ledger.cairo b/exercises/practice/ledger/tests/ledger.cairo index 865cfeb7..2635c133 100644 --- a/exercises/practice/ledger/tests/ledger.cairo +++ b/exercises/practice/ledger/tests/ledger.cairo @@ -1,4 +1,4 @@ -use ledger::{format_entries, Currency, Locale, Entry, date, description, amount_in_cents}; +use ledger::{format_entries, Currency, Locale, Entry}; #[test] fn empty_ledger() { From eacbcc1904ab64ec5c5a9220b1e51854514d26a3 Mon Sep 17 00:00:00 2001 From: Nenad Date: Wed, 25 Dec 2024 20:51:23 +0100 Subject: [PATCH 10/11] Simplify instructions append --- exercises/practice/ledger/.docs/instructions.append.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exercises/practice/ledger/.docs/instructions.append.md b/exercises/practice/ledger/.docs/instructions.append.md index 97c9e71f..82563cd0 100644 --- a/exercises/practice/ledger/.docs/instructions.append.md +++ b/exercises/practice/ledger/.docs/instructions.append.md @@ -1,4 +1,3 @@ # 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. +Because Cairo only supports ASCII characters, and the Euro currency symbol (€) is _not_ an ASCII character, we will be using the character `e` as a substitute. From db24098e309196cfb2b49d39f2ea93e057f6067f Mon Sep 17 00:00:00 2001 From: Nenad Date: Thu, 26 Dec 2024 08:28:54 +0100 Subject: [PATCH 11/11] apply comment suggestions + increase difficulty --- config.json | 2 +- exercises/practice/ledger/.meta/example.cairo | 1 - exercises/practice/ledger/tests/ledger.cairo | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/config.json b/config.json index e691f6fc..07466fd4 100644 --- a/config.json +++ b/config.json @@ -899,7 +899,7 @@ "uuid": "fb9758db-359a-4839-b37e-7a462906846a", "practices": [], "prerequisites": [], - "difficulty": 1 + "difficulty": 4 } ], "foregone": [ diff --git a/exercises/practice/ledger/.meta/example.cairo b/exercises/practice/ledger/.meta/example.cairo index 0987996d..90bfe0ea 100644 --- a/exercises/practice/ledger/.meta/example.cairo +++ b/exercises/practice/ledger/.meta/example.cairo @@ -50,7 +50,6 @@ pub struct Entry { pub amount_in_cents: i32, } - pub fn format_entries( currency: Currency, locale: Locale, entries: Array, ) -> Array { diff --git a/exercises/practice/ledger/tests/ledger.cairo b/exercises/practice/ledger/tests/ledger.cairo index 2635c133..19077be3 100644 --- a/exercises/practice/ledger/tests/ledger.cairo +++ b/exercises/practice/ledger/tests/ledger.cairo @@ -151,7 +151,6 @@ fn dutch_locale_and_euros() { assert_eq!(result, expected); } - #[test] #[ignore] fn dutch_negative_number_with_3_digits_before_decimal_point() {