From 3d8496ec56d2b1de0ec9b4e2ed7981ef1f13aa96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Fernandes?= Date: Sat, 27 Jul 2024 17:58:15 -0300 Subject: [PATCH] [gleam] complete book-store --- gleam/book-store/.gitignore | 4 + gleam/book-store/HELP.md | 32 +++++ gleam/book-store/README.md | 76 ++++++++++ gleam/book-store/gleam.toml | 12 ++ gleam/book-store/manifest.toml | 28 ++++ gleam/book-store/src/book_store.gleam | 32 +++++ gleam/book-store/test/book_store_test.gleam | 151 ++++++++++++++++++++ 7 files changed, 335 insertions(+) create mode 100644 gleam/book-store/.gitignore create mode 100644 gleam/book-store/HELP.md create mode 100644 gleam/book-store/README.md create mode 100644 gleam/book-store/gleam.toml create mode 100644 gleam/book-store/manifest.toml create mode 100644 gleam/book-store/src/book_store.gleam create mode 100644 gleam/book-store/test/book_store_test.gleam diff --git a/gleam/book-store/.gitignore b/gleam/book-store/.gitignore new file mode 100644 index 0000000..170cca9 --- /dev/null +++ b/gleam/book-store/.gitignore @@ -0,0 +1,4 @@ +*.beam +*.ez +build +erl_crash.dump diff --git a/gleam/book-store/HELP.md b/gleam/book-store/HELP.md new file mode 100644 index 0000000..b321e6c --- /dev/null +++ b/gleam/book-store/HELP.md @@ -0,0 +1,32 @@ +# Help + +## Running the tests + +To run the tests, run the command `gleam test` from within the exercise directory. + +## Submitting your solution + +You can submit your solution using the `exercism submit src/book_store.gleam` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Gleam track's documentation](https://exercism.org/docs/tracks/gleam) +- The [Gleam track's programming category on the forum](https://forum.exercism.org/c/programming/gleam) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +To get help if you're having trouble, you can use one of the following resources: + +- [gleam.run](https://gleam.run/documentation/) is the gleam official documentation. +- [Discord](https://discord.gg/Fm8Pwmy) is the discord channel. +- [StackOverflow](https://stackoverflow.com/questions/tagged/gleam) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions. \ No newline at end of file diff --git a/gleam/book-store/README.md b/gleam/book-store/README.md new file mode 100644 index 0000000..10f92bd --- /dev/null +++ b/gleam/book-store/README.md @@ -0,0 +1,76 @@ +# Book Store + +Welcome to Book Store on Exercism's Gleam Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts on multiple book purchases. + +One copy of any of the five books costs $8. + +If, however, you buy two different books, you get a 5% discount on those two books. + +If you buy 3 different books, you get a 10% discount. + +If you buy 4 different books, you get a 20% discount. + +If you buy all 5, you get a 25% discount. + +Note that if you buy four books, of which 3 are different titles, you get a 10% discount on the 3 that form part of a set, but the fourth book still costs $8. + +Your mission is to write code to calculate the price of any conceivable shopping basket (containing only books of the same series), giving as big a discount as possible. + +For example, how much does this basket of books cost? + +- 2 copies of the first book +- 2 copies of the second book +- 2 copies of the third book +- 1 copy of the fourth book +- 1 copy of the fifth book + +One way of grouping these 8 books is: + +- 1 group of 5 (1st, 2nd,3rd, 4th, 5th) +- 1 group of 3 (1st, 2nd, 3rd) + +This would give a total of: + +- 5 books at a 25% discount +- 3 books at a 10% discount + +Resulting in: + +- 5 × (100% - 25%) × $8 = 5 × $6.00 = $30.00, plus +- 3 × (100% - 10%) × $8 = 3 × $7.20 = $21.60 + +Which equals $51.60. + +However, a different way to group these 8 books is: + +- 1 group of 4 books (1st, 2nd, 3rd, 4th) +- 1 group of 4 books (1st, 2nd, 3rd, 5th) + +This would give a total of: + +- 4 books at a 20% discount +- 4 books at a 20% discount + +Resulting in: + +- 4 × (100% - 20%) × $8 = 4 × $6.40 = $25.60, plus +- 4 × (100% - 20%) × $8 = 4 × $6.40 = $25.60 + +Which equals $51.20. + +And $51.20 is the price with the biggest discount. + +## Source + +### Created by + +- @massivefermion + +### Based on + +Inspired by the harry potter kata from Cyber-Dojo. - https://cyber-dojo.org \ No newline at end of file diff --git a/gleam/book-store/gleam.toml b/gleam/book-store/gleam.toml new file mode 100644 index 0000000..b2c0103 --- /dev/null +++ b/gleam/book-store/gleam.toml @@ -0,0 +1,12 @@ +name = "book_store" +version = "0.1.0" + +[dependencies] +gleam_bitwise = "~> 1.2" +gleam_otp = "~> 0.7 or ~> 1.0" +gleam_stdlib = "~> 0.32 or ~> 1.0" +simplifile = "~> 1.0" +gleam_erlang = ">= 0.25.0 and < 1.0.0" + +[dev-dependencies] +exercism_test_runner = "~> 1.4" diff --git a/gleam/book-store/manifest.toml b/gleam/book-store/manifest.toml new file mode 100644 index 0000000..cb4e34e --- /dev/null +++ b/gleam/book-store/manifest.toml @@ -0,0 +1,28 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, + { name = "exercism_test_runner", version = "1.8.0", build_tools = ["gleam"], requirements = ["argv", "gap", "glance", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_stdlib", "simplifile"], otp_app = "exercism_test_runner", source = "hex", outer_checksum = "B944D89A9D049897DF28C63D595D89CB54D8C407D06EFFCE4CDA8C3EC1C9F51E" }, + { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, + { name = "gap", version = "1.1.3", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib"], otp_app = "gap", source = "hex", outer_checksum = "6EF5E3B523FDFBC317E9EA28D5163EE04744A97C007106F90207569789612291" }, + { name = "glance", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "8F3314D27773B7C3B9FB58D8C02C634290422CE531988C0394FA0DF8676B964D" }, + { name = "gleam_bitwise", version = "1.3.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_bitwise", source = "hex", outer_checksum = "B36E1D3188D7F594C7FD4F43D0D2CE17561DE896202017548578B16FE1FE9EFC" }, + { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" }, + { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, + { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, + { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, + { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" }, + { name = "glexer", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "BD477AD657C2B637FEF75F2405FAEFFA533F277A74EF1A5E17B55B1178C228FB" }, + { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, + { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, +] + +[requirements] +exercism_test_runner = { version = "~> 1.4" } +gleam_bitwise = { version = "~> 1.2" } +gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } +gleam_otp = { version = "~> 0.7 or ~> 1.0" } +gleam_stdlib = { version = "~> 0.32 or ~> 1.0" } +simplifile = { version = "~> 1.0" } diff --git a/gleam/book-store/src/book_store.gleam b/gleam/book-store/src/book_store.gleam new file mode 100644 index 0000000..5098752 --- /dev/null +++ b/gleam/book-store/src/book_store.gleam @@ -0,0 +1,32 @@ +import gleam/dict +import gleam/float +import gleam/int +import gleam/list + +pub fn lowest_price(books: List(Int)) -> Float { + books + |> list.group(fn(n) { n }) + |> dict.values + |> list.map(list.length) + |> price(0.0) +} + +fn price(books: List(Int), sum: Float) -> Float { + let books = + list.filter(books, fn(b) { b > 0 }) + |> list.sort(int.compare) + |> list.map(int.subtract(_, 1)) + + case books { + [a] -> sum +. int.to_float(a + 1) *. 800.0 + [_, _] -> price(books, sum +. 2.0 *. 760.0) + [_, _, _] -> price(books, sum +. 3.0 *. 720.0) + [_, _, _, _] -> price(books, sum +. 4.0 *. 640.0) + [a, ..rest] -> + float.min( + price([a + 1, ..rest], sum +. 4.0 *. 640.0), + price([a, ..rest], sum +. 5.0 *. 600.0), + ) + _ -> sum + } +} diff --git a/gleam/book-store/test/book_store_test.gleam b/gleam/book-store/test/book_store_test.gleam new file mode 100644 index 0000000..04396d6 --- /dev/null +++ b/gleam/book-store/test/book_store_test.gleam @@ -0,0 +1,151 @@ +import book_store +import exercism/should +import exercism/test_runner + +pub fn main() { + test_runner.main() +} + +pub fn only_a_single_book_test() { + let input = [1] + let output = book_store.lowest_price(input) + let expected = 800.0 + output + |> should.equal(expected) +} + +pub fn two_of_the_same_book_test() { + let input = [2, 2] + let output = book_store.lowest_price(input) + let expected = 1600.0 + output + |> should.equal(expected) +} + +pub fn empty_basket_test() { + let input = [] + let output = book_store.lowest_price(input) + let expected = 0.0 + output + |> should.equal(expected) +} + +pub fn two_different_books_test() { + let input = [1, 2] + let output = book_store.lowest_price(input) + let expected = 1520.0 + output + |> should.equal(expected) +} + +pub fn three_different_books_test() { + let input = [1, 2, 3] + let output = book_store.lowest_price(input) + let expected = 2160.0 + output + |> should.equal(expected) +} + +pub fn four_different_books_test() { + let input = [1, 2, 3, 4] + let output = book_store.lowest_price(input) + let expected = 2560.0 + output + |> should.equal(expected) +} + +pub fn five_different_books_test() { + let input = [1, 2, 3, 4, 5] + let output = book_store.lowest_price(input) + let expected = 3000.0 + output + |> should.equal(expected) +} + +pub fn two_groups_of_four_is_cheaper_than_group_of_five_plus_group_of_three_test() { + let input = [1, 1, 2, 2, 3, 3, 4, 5] + let output = book_store.lowest_price(input) + let expected = 5120.0 + output + |> should.equal(expected) +} + +pub fn two_groups_of_four_is_cheaper_than_groups_of_five_and_three_test() { + let input = [1, 1, 2, 3, 4, 4, 5, 5] + let output = book_store.lowest_price(input) + let expected = 5120.0 + output + |> should.equal(expected) +} + +pub fn group_of_four_plus_group_of_two_is_cheaper_than_two_groups_of_three_test() { + let input = [1, 1, 2, 2, 3, 4] + let output = book_store.lowest_price(input) + let expected = 4080.0 + output + |> should.equal(expected) +} + +pub fn two_each_of_first_four_books_and_one_copy_each_of_rest_test() { + let input = [1, 1, 2, 2, 3, 3, 4, 4, 5] + let output = book_store.lowest_price(input) + let expected = 5560.0 + output + |> should.equal(expected) +} + +pub fn two_copies_of_each_book_test() { + let input = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5] + let output = book_store.lowest_price(input) + let expected = 6000.0 + output + |> should.equal(expected) +} + +pub fn three_copies_of_first_book_and_two_each_of_remaining_test() { + let input = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1] + let output = book_store.lowest_price(input) + let expected = 6800.0 + output + |> should.equal(expected) +} + +pub fn three_each_of_first_two_books_and_two_each_of_remaining_books_test() { + let input = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2] + let output = book_store.lowest_price(input) + let expected = 7520.0 + output + |> should.equal(expected) +} + +pub fn four_groups_of_four_are_cheaper_than_two_groups_each_of_five_and_three_test() { + let input = [1, 1, 2, 2, 3, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 5] + let output = book_store.lowest_price(input) + let expected = 10_240.0 + output + |> should.equal(expected) +} + +pub fn check_that_groups_of_four_are_created_properly_even_when_there_are_more_groups_of_three_than_groups_of_five_test() { + let input = [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5] + let output = book_store.lowest_price(input) + let expected = 14_560.0 + output + |> should.equal(expected) +} + +pub fn one_group_of_one_and_four_is_cheaper_than_one_group_of_two_and_three_test() { + let input = [1, 1, 2, 3, 4] + let output = book_store.lowest_price(input) + let expected = 3360.0 + output + |> should.equal(expected) +} + +pub fn one_group_of_one_and_two_plus_three_groups_of_four_is_cheaper_than_one_group_of_each_size_test() { + let input = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5] + let output = book_store.lowest_price(input) + let expected = 10_000.0 + output + |> should.equal(expected) +}