From f00dfa5e66dea14b442517672c65b5781899d02c Mon Sep 17 00:00:00 2001
From: bau-mann <17111602+bau-mann@users.noreply.github.com>
Date: Thu, 6 Oct 2022 19:56:53 +0200
Subject: [PATCH 1/3] Initial Implementation Exercise "Book Store"
---
config.json | 8 +
.../practice/book-store/.docs/instructions.md | 68 ++++++
.../practice/book-store/.meta/config.json | 20 ++
.../book-store/.meta/zcl_book_store.clas.abap | 81 +++++++
.../practice/book-store/package.devc.xml | 10 +
.../book-store/zcl_book_store.clas.abap | 34 +++
.../zcl_book_store.clas.testclasses.abap | 207 ++++++++++++++++++
.../book-store/zcl_book_store.clas.xml | 17 ++
8 files changed, 445 insertions(+)
create mode 100644 exercises/practice/book-store/.docs/instructions.md
create mode 100644 exercises/practice/book-store/.meta/config.json
create mode 100644 exercises/practice/book-store/.meta/zcl_book_store.clas.abap
create mode 100644 exercises/practice/book-store/package.devc.xml
create mode 100644 exercises/practice/book-store/zcl_book_store.clas.abap
create mode 100644 exercises/practice/book-store/zcl_book_store.clas.testclasses.abap
create mode 100644 exercises/practice/book-store/zcl_book_store.clas.xml
diff --git a/config.json b/config.json
index 8fe85f3c..87e09b09 100644
--- a/config.json
+++ b/config.json
@@ -280,6 +280,14 @@
"practices": [],
"prerequisites": [],
"difficulty": 4
+ },
+ {
+ "slug": "book-store",
+ "name": "Book Store",
+ "uuid": "0ceab982-40cd-48b3-be49-0fc1128708fd",
+ "practices": [],
+ "prerequisites": [],
+ "difficulty": 7
}
]
},
diff --git a/exercises/practice/book-store/.docs/instructions.md b/exercises/practice/book-store/.docs/instructions.md
new file mode 100644
index 00000000..cce9deea
--- /dev/null
+++ b/exercises/practice/book-store/.docs/instructions.md
@@ -0,0 +1,68 @@
+# 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 a piece of 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 --> 25% discount (1st,2nd,3rd,4th,5th)
+- +1 group of 3 --> 10% discount (1st,2nd,3rd)
+
+This would give a total of:
+
+- 5 books at a 25% discount
+- +3 books at a 10% discount
+
+Resulting in:
+
+- 5 × (8 - 2.00) = 5 × 6.00 = $30.00
+- +3 × (8 - 0.80) = 3 × 7.20 = $21.60
+
+For a total of $51.60
+
+However, a different way to group these 8 books is:
+
+- 1 group of 4 books --> 20% discount (1st,2nd,3rd,4th)
+- +1 group of 4 books --> 20% discount (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 × (8 - 1.60) = 4 × 6.40 = $25.60
+- +4 × (8 - 1.60) = 4 × 6.40 = $25.60
+
+For a total of $51.20
+
+And $51.20 is the price with the biggest discount.
diff --git a/exercises/practice/book-store/.meta/config.json b/exercises/practice/book-store/.meta/config.json
new file mode 100644
index 00000000..e1f0e645
--- /dev/null
+++ b/exercises/practice/book-store/.meta/config.json
@@ -0,0 +1,20 @@
+{
+ "blurb": "To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts of multiple-book purchases.",
+ "authors": [
+ "bau-mann"
+ ],
+ "contributors": [],
+ "files": {
+ "solution": [
+ "zcl_book_store.clas.abap"
+ ],
+ "test": [
+ "zcl_book_store.clas.testclasses.abap"
+ ],
+ "example": [
+ ".meta/zcl_book_store.clas.abap"
+ ]
+ },
+ "source": "Inspired by the harry potter kata from Cyber-Dojo.",
+ "source_url": "http://cyber-dojo.org"
+}
diff --git a/exercises/practice/book-store/.meta/zcl_book_store.clas.abap b/exercises/practice/book-store/.meta/zcl_book_store.clas.abap
new file mode 100644
index 00000000..451acde5
--- /dev/null
+++ b/exercises/practice/book-store/.meta/zcl_book_store.clas.abap
@@ -0,0 +1,81 @@
+CLASS zcl_book_store DEFINITION
+ PUBLIC
+ FINAL
+ CREATE PUBLIC .
+
+ PUBLIC SECTION.
+
+ "! ID of book to buy from 1 to 5
+ TYPES book_id TYPE i.
+
+ TYPES basket_type TYPE SORTED TABLE OF book_id
+ WITH NON-UNIQUE KEY table_line.
+
+ TYPES total TYPE p LENGTH 3 DECIMALS 2.
+
+ "! @parameter basket | E.g., buying two copies of the first book and one copy of the second book
+ "! is equivalent to ( ( 1 ) ( 1 ) ( 2 ) )
+ METHODS calculate_total
+ IMPORTING basket TYPE basket_type
+ RETURNING VALUE(total) TYPE total.
+
+ PROTECTED SECTION.
+ PRIVATE SECTION.
+ENDCLASS.
+
+
+
+CLASS zcl_book_store IMPLEMENTATION.
+
+ METHOD calculate_total.
+
+ TYPES: BEGIN OF basket_aggr_line,
+ book_id TYPE book_id,
+ quantity TYPE i,
+ END OF basket_aggr_line.
+ DATA basket_aggr TYPE TABLE OF basket_aggr_line
+ WITH NON-UNIQUE SORTED KEY quantity COMPONENTS quantity.
+
+ TYPES: BEGIN OF discount_grp,
+ unique_books TYPE i,
+ discount TYPE total,
+ END OF discount_grp.
+ DATA grp TYPE REF TO discount_grp.
+ DATA discount_grps TYPE TABLE OF discount_grp.
+ discount_grps = VALUE #( ( unique_books = 5 discount = '.25' )
+ ( unique_books = 4 discount = '.20' )
+ ( unique_books = 3 discount = '.10' )
+ ( unique_books = 2 discount = '.05' )
+ ( unique_books = 1 discount = 0 ) ).
+
+ DATA new_total TYPE total.
+ total = 999.
+ DATA(max_grp) = 5.
+
+ DO 5 TIMES.
+
+ new_total = 0.
+ basket_aggr = VALUE #( FOR i = 1 UNTIL i > 5
+ ( book_id = i
+ quantity = lines( FILTER #( basket WHERE table_line = i ) ) ) ).
+
+ LOOP AT discount_grps REFERENCE INTO grp.
+ IF max_grp >= grp->unique_books.
+ WHILE lines( FILTER #( basket_aggr USING KEY quantity WHERE quantity > 0 ) ) >= grp->unique_books.
+ SORT basket_aggr BY quantity DESCENDING.
+ DO grp->unique_books TIMES.
+ basket_aggr[ sy-index ]-quantity -= 1.
+ ENDDO.
+ new_total += grp->unique_books * 8 * ( 1 - grp->discount ).
+ ENDWHILE.
+ ENDIF.
+ ENDLOOP.
+
+ total = nmin( val1 = total val2 = new_total ).
+ max_grp -= 1.
+
+ ENDDO.
+
+ ENDMETHOD.
+
+ENDCLASS.
diff --git a/exercises/practice/book-store/package.devc.xml b/exercises/practice/book-store/package.devc.xml
new file mode 100644
index 00000000..b648ead0
--- /dev/null
+++ b/exercises/practice/book-store/package.devc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+ Exercism: Book Store
+
+
+
+
diff --git a/exercises/practice/book-store/zcl_book_store.clas.abap b/exercises/practice/book-store/zcl_book_store.clas.abap
new file mode 100644
index 00000000..7b70218f
--- /dev/null
+++ b/exercises/practice/book-store/zcl_book_store.clas.abap
@@ -0,0 +1,34 @@
+CLASS zcl_book_store DEFINITION
+ PUBLIC
+ FINAL
+ CREATE PUBLIC .
+
+ PUBLIC SECTION.
+
+ "! ID of book to buy from 1 to 5
+ TYPES book_id TYPE i.
+
+ TYPES basket_type TYPE SORTED TABLE OF book_id
+ WITH NON-UNIQUE KEY table_line.
+
+ TYPES total TYPE p LENGTH 3 DECIMALS 2.
+
+ "! @parameter basket | E.g., buying two copies of the first book and one copy of the second book
+ "! is equivalent to ( ( 1 ) ( 1 ) ( 2 ) )
+ METHODS calculate_total
+ IMPORTING basket TYPE basket_type
+ RETURNING VALUE(total) TYPE total.
+
+ PROTECTED SECTION.
+ PRIVATE SECTION.
+ENDCLASS.
+
+
+
+CLASS zcl_book_store IMPLEMENTATION.
+
+ METHOD calculate_total.
+ " add solution here
+ ENDMETHOD.
+
+ENDCLASS.
diff --git a/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap b/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap
new file mode 100644
index 00000000..93dbc6d3
--- /dev/null
+++ b/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap
@@ -0,0 +1,207 @@
+CLASS ltcl_book_store DEFINITION FINAL FOR TESTING
+ DURATION SHORT
+ RISK LEVEL HARMLESS.
+
+ PRIVATE SECTION.
+
+ DATA cut TYPE REF TO zcl_book_store.
+
+ METHODS setup.
+
+ METHODS only_a_single_book FOR TESTING RAISING cx_static_check.
+ METHODS two_of_the_same_book FOR TESTING RAISING cx_static_check.
+ METHODS empty_basket FOR TESTING RAISING cx_static_check.
+ METHODS two_different_books FOR TESTING RAISING cx_static_check.
+ METHODS three_different_books FOR TESTING RAISING cx_static_check.
+ METHODS four_different_books FOR TESTING RAISING cx_static_check.
+ METHODS five_different_books FOR TESTING RAISING cx_static_check.
+ "! Two groups of four is cheaper than group of five plus group of three
+ METHODS two_groups_of_four_1 FOR TESTING RAISING cx_static_check.
+ "! Two groups of four is cheaper than groups of five and three
+ METHODS two_groups_of_four_2 FOR TESTING RAISING cx_static_check.
+ "! Group of four plus group of two is cheaper than two groups of three
+ METHODS group_of_four_and_group_of_two FOR TESTING RAISING cx_static_check.
+ "! Two each of first four books and one copy each of rest
+ METHODS two_copies_of_each_except_one FOR TESTING RAISING cx_static_check.
+ METHODS two_copies_of_each_book FOR TESTING RAISING cx_static_check.
+ "! Three copies of first book and two each of remaining
+ METHODS three_of_first_two_each_remain FOR TESTING RAISING cx_static_check.
+ "! Three each of first two books and two each of remaining books
+ METHODS three_of_first_two_two_of_rem FOR TESTING RAISING cx_static_check.
+ "! Four groups of four are cheaper than two groups each of five and three
+ METHODS four_groups_of_four FOR TESTING RAISING cx_static_check.
+ "! Check that groups of four are created properly even when there are more groups of three than groups of five
+ METHODS groups_of_four FOR TESTING RAISING cx_static_check.
+ "!One group of one and four is cheaper than one group of two and three
+ METHODS one_group_of_one_and_four FOR TESTING RAISING cx_static_check.
+ "! One group of one and two plus three groups of four is cheaper than one group of each size"
+ METHODS mixed_1 FOR TESTING RAISING cx_static_check.
+
+ENDCLASS.
+
+
+CLASS ltcl_book_store IMPLEMENTATION.
+
+ METHOD setup.
+ cut = NEW #( ).
+ ENDMETHOD.
+
+ METHOD only_a_single_book.
+ cl_abap_unit_assert=>assert_equals(
+ exp = 8
+ act = cut->calculate_total( VALUE #( ( 1 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD two_of_the_same_book.
+ cl_abap_unit_assert=>assert_equals(
+ exp = 16
+ act = cut->calculate_total( VALUE #( ( 2 ) ( 2 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD empty_basket.
+ cl_abap_unit_assert=>assert_equals(
+ exp = 0
+ act = cut->calculate_total( VALUE #( ) ) ).
+ ENDMETHOD.
+
+ METHOD two_different_books.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '15.2'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 2 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD three_different_books.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '21.6'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 2 ) ( 3 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD four_different_books.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '25.6'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD five_different_books.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '30'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD two_groups_of_four_1.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '51.2'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 )
+ ( 2 ) ( 2 )
+ ( 3 ) ( 3 )
+ ( 4 )
+ ( 5 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD two_groups_of_four_2.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '51.2'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 )
+ ( 2 )
+ ( 3 )
+ ( 4 ) ( 4 )
+ ( 5 ) ( 5 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD group_of_four_and_group_of_two.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '40.8'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 )
+ ( 2 ) ( 2 )
+ ( 3 )
+ ( 4 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD two_copies_of_each_except_one.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '55.6'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 )
+ ( 2 ) ( 2 )
+ ( 3 ) ( 3 )
+ ( 4 ) ( 4 )
+ ( 5 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD two_copies_of_each_book.
+ cl_abap_unit_assert=>assert_equals(
+ exp = 60
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 )
+ ( 2 ) ( 2 )
+ ( 3 ) ( 3 )
+ ( 4 ) ( 4 )
+ ( 5 ) ( 5 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD three_of_first_two_each_remain.
+ cl_abap_unit_assert=>assert_equals(
+ exp = 68
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 )
+ ( 2 ) ( 2 )
+ ( 3 ) ( 3 )
+ ( 4 ) ( 4 )
+ ( 5 ) ( 5 )
+ ( 1 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD three_of_first_two_two_of_rem.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '75.2'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 )
+ ( 2 ) ( 2 )
+ ( 3 ) ( 3 )
+ ( 4 ) ( 4 )
+ ( 5 ) ( 5 )
+ ( 1 )
+ ( 2 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD four_groups_of_four.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '102.4'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 )
+ ( 2 ) ( 2 )
+ ( 3 ) ( 3 )
+ ( 4 )
+ ( 5 )
+ ( 1 ) ( 1 )
+ ( 2 ) ( 2 )
+ ( 3 ) ( 3 )
+ ( 4 )
+ ( 5 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD groups_of_four.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '145.6'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) ( 1 ) ( 1 ) ( 1 ) ( 1 )
+ ( 2 ) ( 2 ) ( 2 ) ( 2 ) ( 2 ) ( 2 )
+ ( 3 ) ( 3 ) ( 3 ) ( 3 ) ( 3 ) ( 3 )
+ ( 4 ) ( 4 )
+ ( 5 ) ( 5 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD one_group_of_one_and_four.
+ cl_abap_unit_assert=>assert_equals(
+ exp = '33.6'
+ act = cut->calculate_total( VALUE #( ( 1 ) ( 1 )
+ ( 2 )
+ ( 3 )
+ ( 4 ) ) ) ).
+ ENDMETHOD.
+
+ METHOD mixed_1.
+ cl_abap_unit_assert=>assert_equals(
+ exp = 100
+ act = cut->calculate_total( VALUE #( ( 1 )
+ ( 2 ) ( 2 )
+ ( 3 ) ( 3 ) ( 3 )
+ ( 4 ) ( 4 ) ( 4 ) ( 4 )
+ ( 5 ) ( 5 ) ( 5 ) ( 5 ) ( 5 ) ) ) ).
+ ENDMETHOD.
+
+ENDCLASS.
diff --git a/exercises/practice/book-store/zcl_book_store.clas.xml b/exercises/practice/book-store/zcl_book_store.clas.xml
new file mode 100644
index 00000000..1dad2a61
--- /dev/null
+++ b/exercises/practice/book-store/zcl_book_store.clas.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ ZCL_BOOK_STORE
+ E
+ Exercism: Book Store
+ 1
+ X
+ X
+ X
+ X
+
+
+
+
From 90bb02fbc44d3858190ba5cb0fb7d6b151acc6fc Mon Sep 17 00:00:00 2001
From: bau-mann <17111602+bau-mann@users.noreply.github.com>
Date: Thu, 6 Oct 2022 20:02:59 +0200
Subject: [PATCH 2/3] Fix typo in test class
---
.../practice/book-store/zcl_book_store.clas.testclasses.abap | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap b/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap
index 93dbc6d3..14dcbc17 100644
--- a/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap
+++ b/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap
@@ -34,7 +34,7 @@ CLASS ltcl_book_store DEFINITION FINAL FOR TESTING
METHODS groups_of_four FOR TESTING RAISING cx_static_check.
"!One group of one and four is cheaper than one group of two and three
METHODS one_group_of_one_and_four FOR TESTING RAISING cx_static_check.
- "! One group of one and two plus three groups of four is cheaper than one group of each size"
+ "! One group of one and two plus three groups of four is cheaper than one group of each size
METHODS mixed_1 FOR TESTING RAISING cx_static_check.
ENDCLASS.
From c47d891a089cc14dc48359722b47f5db598072c7 Mon Sep 17 00:00:00 2001
From: bau-mann <17111602+bau-mann@users.noreply.github.com>
Date: Wed, 12 Oct 2022 20:32:29 +0200
Subject: [PATCH 3/3] Small Change to Reference Solution
---
.../book-store/.meta/zcl_book_store.clas.abap | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/exercises/practice/book-store/.meta/zcl_book_store.clas.abap b/exercises/practice/book-store/.meta/zcl_book_store.clas.abap
index 451acde5..7266a6e5 100644
--- a/exercises/practice/book-store/.meta/zcl_book_store.clas.abap
+++ b/exercises/practice/book-store/.meta/zcl_book_store.clas.abap
@@ -59,16 +59,14 @@ CLASS zcl_book_store IMPLEMENTATION.
( book_id = i
quantity = lines( FILTER #( basket WHERE table_line = i ) ) ) ).
- LOOP AT discount_grps REFERENCE INTO grp.
- IF max_grp >= grp->unique_books.
- WHILE lines( FILTER #( basket_aggr USING KEY quantity WHERE quantity > 0 ) ) >= grp->unique_books.
- SORT basket_aggr BY quantity DESCENDING.
- DO grp->unique_books TIMES.
- basket_aggr[ sy-index ]-quantity -= 1.
- ENDDO.
- new_total += grp->unique_books * 8 * ( 1 - grp->discount ).
- ENDWHILE.
- ENDIF.
+ LOOP AT discount_grps REFERENCE INTO grp WHERE unique_books <= max_grp.
+ WHILE lines( FILTER #( basket_aggr USING KEY quantity WHERE quantity > 0 ) ) >= grp->unique_books.
+ SORT basket_aggr BY quantity DESCENDING.
+ DO grp->unique_books TIMES.
+ basket_aggr[ sy-index ]-quantity -= 1.
+ ENDDO.
+ new_total += grp->unique_books * 8 * ( 1 - grp->discount ).
+ ENDWHILE.
ENDLOOP.
total = nmin( val1 = total val2 = new_total ).