trackler 2.2.1.62 → 2.2.1.63

Sign up to get free protection for your applications and to get access to all the features.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/all-your-base/canonical-data.json +20 -29
  4. data/problem-specifications/exercises/forth/canonical-data.json +52 -5
  5. data/problem-specifications/exercises/isbn-verifier/canonical-data.json +15 -15
  6. data/problem-specifications/exercises/protein-translation/canonical-data.json +153 -0
  7. data/problem-specifications/exercises/reverse-string/canonical-data.json +39 -0
  8. data/problem-specifications/exercises/reverse-string/description.md +5 -0
  9. data/problem-specifications/exercises/reverse-string/metadata.yml +5 -0
  10. data/tracks/c/config.json +42 -41
  11. data/tracks/csharp/config.json +13 -0
  12. data/tracks/csharp/exercises/Exercises.sln +6 -0
  13. data/tracks/csharp/exercises/isbn-verifier/Example.cs +27 -0
  14. data/tracks/csharp/exercises/isbn-verifier/IsbnVerifier.cs +9 -0
  15. data/tracks/csharp/exercises/isbn-verifier/IsbnVerifier.csproj +19 -0
  16. data/tracks/csharp/exercises/isbn-verifier/IsbnVerifierTest.cs +84 -0
  17. data/tracks/csharp/exercises/isbn-verifier/README.md +40 -0
  18. data/tracks/csharp/generators/Exercises/IsbnVerifier.cs +15 -0
  19. data/tracks/delphi/config.json +28 -28
  20. data/tracks/erlang/config.json +10 -0
  21. data/tracks/erlang/exercises/accumulate/rebar.config +1 -1
  22. data/tracks/erlang/exercises/accumulate/test/accumulate_tests.erl +9 -7
  23. data/tracks/erlang/exercises/all-your-base/rebar.config +1 -1
  24. data/tracks/erlang/exercises/all-your-base/test/all_your_base_tests.erl +26 -24
  25. data/tracks/erlang/exercises/allergies/rebar.config +1 -1
  26. data/tracks/erlang/exercises/allergies/test/allergies_tests.erl +19 -17
  27. data/tracks/erlang/exercises/anagram/rebar.config +1 -3
  28. data/tracks/erlang/exercises/anagram/test/anagram_tests.erl +14 -12
  29. data/tracks/erlang/exercises/atbash-cipher/rebar.config +1 -1
  30. data/tracks/erlang/exercises/atbash-cipher/test/atbash_cipher_tests.erl +7 -5
  31. data/tracks/erlang/exercises/bank-account/rebar.config +1 -1
  32. data/tracks/erlang/exercises/bank-account/test/bank_account_tests.erl +52 -51
  33. data/tracks/erlang/exercises/beer-song/rebar.config +30 -0
  34. data/tracks/erlang/exercises/beer-song/test/beer_song_tests.erl +11 -10
  35. data/tracks/erlang/exercises/bob/rebar.config +1 -1
  36. data/tracks/erlang/exercises/bob/test/bob_tests.erl +33 -35
  37. data/tracks/erlang/exercises/circular-buffer/rebar.config +1 -1
  38. data/tracks/erlang/exercises/circular-buffer/test/circular_buffer_tests.erl +33 -32
  39. data/tracks/erlang/exercises/clock/rebar.config +1 -1
  40. data/tracks/erlang/exercises/clock/test/clock_tests.erl +57 -50
  41. data/tracks/erlang/exercises/collatz-conjecture/rebar.config +1 -1
  42. data/tracks/erlang/exercises/collatz-conjecture/test/collatz_conjecture_tests.erl +11 -9
  43. data/tracks/erlang/exercises/complex-numbers/rebar.config +1 -1
  44. data/tracks/erlang/exercises/complex-numbers/test/complex_numbers_tests.erl +102 -76
  45. data/tracks/erlang/exercises/custom-set/rebar.config +1 -1
  46. data/tracks/erlang/exercises/custom-set/test/custom_set_tests.erl +100 -109
  47. data/tracks/erlang/exercises/difference-of-squares/rebar.config +1 -1
  48. data/tracks/erlang/exercises/difference-of-squares/test/difference_of_squares_tests.erl +8 -7
  49. data/tracks/erlang/exercises/etl/rebar.config +1 -1
  50. data/tracks/erlang/exercises/etl/test/etl_tests.erl +6 -5
  51. data/tracks/erlang/exercises/gigasecond/rebar.config +1 -1
  52. data/tracks/erlang/exercises/gigasecond/test/gigasecond_tests.erl +9 -8
  53. data/tracks/erlang/exercises/grade-school/rebar.config +1 -1
  54. data/tracks/erlang/exercises/grade-school/test/grade_school_tests.erl +27 -23
  55. data/tracks/erlang/exercises/grains/rebar.config +1 -1
  56. data/tracks/erlang/exercises/grains/test/grains_tests.erl +7 -6
  57. data/tracks/erlang/exercises/hamming/rebar.config +1 -1
  58. data/tracks/erlang/exercises/hamming/test/hamming_tests.erl +20 -23
  59. data/tracks/erlang/exercises/hello-world/rebar.config +1 -1
  60. data/tracks/erlang/exercises/hello-world/test/hello_world_tests.erl +6 -4
  61. data/tracks/erlang/exercises/isogram/rebar.config +1 -1
  62. data/tracks/erlang/exercises/isogram/test/isogram_tests.erl +14 -12
  63. data/tracks/erlang/exercises/largest-series-product/rebar.config +1 -1
  64. data/tracks/erlang/exercises/largest-series-product/test/largest_series_product_tests.erl +22 -21
  65. data/tracks/erlang/exercises/leap/rebar.config +1 -1
  66. data/tracks/erlang/exercises/leap/test/leap_tests.erl +8 -7
  67. data/tracks/erlang/exercises/luhn/rebar.config +1 -1
  68. data/tracks/erlang/exercises/luhn/test/luhn_tests.erl +18 -16
  69. data/tracks/erlang/exercises/meetup/rebar.config +1 -1
  70. data/tracks/erlang/exercises/meetup/test/meetup_tests.erl +96 -95
  71. data/tracks/erlang/exercises/nucleotide-count/rebar.config +1 -1
  72. data/tracks/erlang/exercises/nucleotide-count/test/nucleotide_count_tests.erl +12 -11
  73. data/tracks/erlang/exercises/pangram/README.md +60 -0
  74. data/tracks/erlang/exercises/{accumulate → pangram}/include/exercism.hrl +0 -0
  75. data/tracks/erlang/exercises/{beer-song/rebar.conf → pangram/rebar.config} +0 -0
  76. data/tracks/erlang/exercises/pangram/src/example.erl +11 -0
  77. data/tracks/erlang/exercises/pangram/src/pangram.app.src +9 -0
  78. data/tracks/erlang/exercises/pangram/src/pangram.erl +8 -0
  79. data/tracks/erlang/exercises/pangram/test/pangram_tests.erl +38 -0
  80. data/tracks/erlang/exercises/parallel-letter-frequency/rebar.config +1 -1
  81. data/tracks/erlang/exercises/parallel-letter-frequency/test/parallel_letter_frequency_tests.erl +7 -6
  82. data/tracks/erlang/exercises/phone-number/rebar.config +1 -1
  83. data/tracks/erlang/exercises/phone-number/test/phone_number_tests.erl +13 -12
  84. data/tracks/erlang/exercises/rna-transcription/rebar.config +1 -1
  85. data/tracks/erlang/exercises/rna-transcription/test/rna_transcription_tests.erl +13 -11
  86. data/tracks/erlang/exercises/robot-simulator/rebar.config +1 -1
  87. data/tracks/erlang/exercises/robot-simulator/test/robot_simulator_tests.erl +70 -69
  88. data/tracks/erlang/exercises/roman-numerals/rebar.config +1 -1
  89. data/tracks/erlang/exercises/roman-numerals/test/roman_numerals_tests.erl +6 -5
  90. data/tracks/erlang/exercises/rotational-cipher/rebar.config +1 -8
  91. data/tracks/erlang/exercises/rotational-cipher/test/rotational_cipher_tests.erl +29 -27
  92. data/tracks/erlang/exercises/scrabble-score/rebar.config +1 -1
  93. data/tracks/erlang/exercises/scrabble-score/test/scrabble_score_tests.erl +6 -5
  94. data/tracks/erlang/exercises/series/rebar.config +1 -1
  95. data/tracks/erlang/exercises/series/test/series_tests.erl +6 -5
  96. data/tracks/erlang/exercises/sieve/rebar.config +1 -1
  97. data/tracks/erlang/exercises/sieve/test/sieve_tests.erl +10 -8
  98. data/tracks/erlang/exercises/space-age/rebar.config +1 -1
  99. data/tracks/erlang/exercises/space-age/test/space_age_tests.erl +20 -19
  100. data/tracks/erlang/exercises/spiral-matrix/rebar.config +1 -1
  101. data/tracks/erlang/exercises/spiral-matrix/test/spiral_matrix_tests.erl +12 -10
  102. data/tracks/erlang/exercises/strain/rebar.config +1 -1
  103. data/tracks/erlang/exercises/strain/test/strain_tests.erl +17 -16
  104. data/tracks/erlang/exercises/sum-of-multiples/rebar.config +1 -1
  105. data/tracks/erlang/exercises/sum-of-multiples/test/sum_of_multiples_tests.erl +16 -15
  106. data/tracks/erlang/exercises/triangle/rebar.config +1 -1
  107. data/tracks/erlang/exercises/triangle/test/triangle_tests.erl +20 -19
  108. data/tracks/erlang/exercises/two-fer/rebar.config +1 -1
  109. data/tracks/erlang/exercises/two-fer/test/two_fer_tests.erl +8 -7
  110. data/tracks/erlang/exercises/word-count/rebar.config +1 -1
  111. data/tracks/erlang/exercises/word-count/test/word_count_tests.erl +6 -5
  112. data/tracks/erlang/exercises/zipper/rebar.config +1 -1
  113. data/tracks/erlang/exercises/zipper/test/zipper_tests.erl +47 -46
  114. data/tracks/erlang/testgen/src/tgen.erl +8 -6
  115. data/tracks/erlang/testgen/src/tgen_bob.erl +1 -1
  116. data/tracks/erlang/testgen/src/tgen_collatz-conjecture.erl +2 -2
  117. data/tracks/erlang/testgen/src/tgen_complex-numbers.erl +11 -11
  118. data/tracks/erlang/testgen/src/tgen_custom-set.erl +14 -14
  119. data/tracks/erlang/testgen/src/tgen_hamming.erl +2 -2
  120. data/tracks/erlang/testgen/src/tgen_hello-world.erl +1 -1
  121. data/tracks/erlang/testgen/src/tgen_leap.erl +1 -1
  122. data/tracks/erlang/testgen/src/tgen_rna-transcription.erl +2 -2
  123. data/tracks/erlang/testgen/src/tgs.erl +6 -0
  124. data/tracks/fsharp/exercises/beer-song/BeerSong.fs +1 -5
  125. data/tracks/fsharp/exercises/beer-song/BeerSongTest.fs +342 -20
  126. data/tracks/fsharp/exercises/beer-song/Example.fs +16 -10
  127. data/tracks/fsharp/exercises/food-chain/Example.fs +1 -1
  128. data/tracks/fsharp/exercises/food-chain/FoodChain.fs +1 -3
  129. data/tracks/fsharp/exercises/food-chain/FoodChainTest.fs +11 -11
  130. data/tracks/fsharp/exercises/house/Example.fs +1 -1
  131. data/tracks/fsharp/exercises/house/House.fs +1 -3
  132. data/tracks/fsharp/exercises/house/HouseTest.fs +202 -202
  133. data/tracks/fsharp/exercises/proverb/Example.fs +9 -9
  134. data/tracks/fsharp/exercises/proverb/Proverb.fs +1 -3
  135. data/tracks/fsharp/exercises/proverb/ProverbTest.fs +50 -21
  136. data/tracks/fsharp/exercises/twelve-days/Example.fs +4 -4
  137. data/tracks/fsharp/exercises/twelve-days/TwelveDays.fs +2 -6
  138. data/tracks/fsharp/exercises/twelve-days/TwelveDaysTest.fs +81 -57
  139. data/tracks/fsharp/generators/Generators.fs +39 -15
  140. data/tracks/go/bin/run-generators +5 -1
  141. data/tracks/go/config.json +14 -2
  142. data/tracks/go/exercises/accumulate/example.go +2 -1
  143. data/tracks/go/exercises/zebra-puzzle/.meta/hints.md +24 -0
  144. data/tracks/go/exercises/zebra-puzzle/README.md +76 -0
  145. data/tracks/go/exercises/zebra-puzzle/example.go +256 -0
  146. data/tracks/go/exercises/zebra-puzzle/zebra_puzzle_test.go +18 -0
  147. data/tracks/haskell/exercises/forth/package.yaml +1 -1
  148. data/tracks/haskell/exercises/forth/test/Tests.hs +1 -4
  149. data/tracks/haskell/exercises/pascals-triangle/package.yaml +1 -1
  150. data/tracks/haskell/exercises/pascals-triangle/test/Tests.hs +22 -1
  151. data/tracks/java/POLICIES.md +3 -3
  152. data/tracks/java/config.json +21 -2
  153. data/tracks/java/exercises/atbash-cipher/.meta/src/reference/java/Atbash.java +12 -12
  154. data/tracks/java/exercises/protein-translation/.meta/src/reference/java/ProteinTranslator.java +47 -0
  155. data/tracks/java/exercises/protein-translation/.meta/version +1 -0
  156. data/tracks/java/exercises/protein-translation/README.md +58 -0
  157. data/tracks/java/exercises/protein-translation/build.gradle +18 -0
  158. data/tracks/java/exercises/protein-translation/src/main/java/ProteinTranslator.java +8 -0
  159. data/tracks/java/exercises/protein-translation/src/test/java/ProteinTranslatorTest.java +179 -0
  160. data/tracks/java/exercises/settings.gradle +1 -0
  161. data/tracks/javascript/.eslintignore +2 -2
  162. data/tracks/javascript/exercises/accumulate/example.js +1 -2
  163. data/tracks/javascript/exercises/grains/example.js +6 -3
  164. data/tracks/kotlin/exercises/beer-song/.meta/src/reference/kotlin/BeerSong.kt +3 -3
  165. data/tracks/kotlin/exercises/beer-song/.meta/version +1 -1
  166. data/tracks/kotlin/exercises/beer-song/README.md +1 -1
  167. data/tracks/kotlin/exercises/beer-song/src/test/kotlin/BeerSongTest.kt +6 -6
  168. data/tracks/kotlin/exercises/forth/.meta/version +1 -1
  169. data/tracks/kotlin/exercises/forth/src/test/kotlin/ForthEvaluatorTest.kt +0 -7
  170. data/tracks/kotlin/exercises/meetup/README.md +16 -12
  171. data/tracks/kotlin/exercises/nth-prime/.meta/src/reference/kotlin/Prime.kt +7 -2
  172. data/tracks/kotlin/exercises/nth-prime/.meta/version +1 -1
  173. data/tracks/kotlin/exercises/nth-prime/src/test/kotlin/PrimeTest.kt +10 -1
  174. data/tracks/kotlin/exercises/nucleotide-count/README.md +2 -2
  175. data/tracks/kotlin/exercises/pascals-triangle/.meta/src/reference/kotlin/PascalsTriangle.kt +1 -1
  176. data/tracks/kotlin/exercises/pascals-triangle/.meta/version +1 -1
  177. data/tracks/kotlin/exercises/pascals-triangle/src/test/kotlin/PascalsTriangleTest.kt +58 -1
  178. data/tracks/kotlin/exercises/sum-of-multiples/README.md +3 -3
  179. data/tracks/ocaml/exercises/forth/test.ml +1 -3
  180. data/tracks/ocaml/exercises/rectangles/example.ml +11 -11
  181. data/tracks/php/exercises/bob/example.php +2 -2
  182. data/tracks/purescript/config.json +3 -3
  183. data/tracks/python/config.json +34 -22
  184. data/tracks/python/exercises/alphametics/example.py +90 -34
  185. data/tracks/python/exercises/ocr-numbers/example.py +18 -21
  186. data/tracks/python/exercises/ocr-numbers/ocr_numbers.py +1 -5
  187. data/tracks/python/exercises/ocr-numbers/ocr_numbers_test.py +124 -106
  188. data/tracks/python/exercises/simple-linked-list/README.md +49 -0
  189. data/tracks/python/exercises/simple-linked-list/example.py +67 -0
  190. data/tracks/python/exercises/simple-linked-list/hints.md +10 -0
  191. data/tracks/python/exercises/simple-linked-list/simple_linked_list.py +33 -0
  192. data/tracks/python/exercises/simple-linked-list/simple_linked_list_test.py +112 -0
  193. data/tracks/rust/README.md +2 -0
  194. data/tracks/rust/bin/init_exercise.py +586 -0
  195. data/tracks/rust/config.json +20 -10
  196. data/tracks/rust/exercises/book-store/.gitignore +3 -0
  197. data/tracks/rust/exercises/book-store/Cargo-example.toml +7 -0
  198. data/tracks/rust/exercises/book-store/Cargo.toml +6 -0
  199. data/tracks/rust/exercises/book-store/README.md +107 -0
  200. data/tracks/rust/exercises/book-store/example.rs +187 -0
  201. data/tracks/rust/exercises/book-store/src/lib.rs +3 -0
  202. data/tracks/rust/exercises/book-store/tests/book-store.rs +130 -0
  203. data/tracks/sml/config.json +8 -8
  204. data/tracks/typescript/config.json +6 -6
  205. metadata +43 -46
  206. data/tracks/erlang/exercises/all-your-base/include/exercism.hrl +0 -11
  207. data/tracks/erlang/exercises/allergies/include/exercism.hrl +0 -11
  208. data/tracks/erlang/exercises/anagram/include/exercism.hrl +0 -11
  209. data/tracks/erlang/exercises/atbash-cipher/include/exercism.hrl +0 -11
  210. data/tracks/erlang/exercises/bank-account/include/exercism.hrl +0 -11
  211. data/tracks/erlang/exercises/beer-song/include/exercism.hrl +0 -11
  212. data/tracks/erlang/exercises/bob/include/exercism.hrl +0 -11
  213. data/tracks/erlang/exercises/circular-buffer/include/exercism.hrl +0 -11
  214. data/tracks/erlang/exercises/clock/include/exercism.hrl +0 -11
  215. data/tracks/erlang/exercises/collatz-conjecture/include/exercism.hrl +0 -11
  216. data/tracks/erlang/exercises/complex-numbers/include/exercism.hrl +0 -11
  217. data/tracks/erlang/exercises/custom-set/include/exercism.hrl +0 -11
  218. data/tracks/erlang/exercises/difference-of-squares/include/exercism.hrl +0 -11
  219. data/tracks/erlang/exercises/etl/include/exercism.hrl +0 -11
  220. data/tracks/erlang/exercises/gigasecond/include/exercism.hrl +0 -11
  221. data/tracks/erlang/exercises/grade-school/include/exercism.hrl +0 -11
  222. data/tracks/erlang/exercises/grains/include/exercism.hrl +0 -11
  223. data/tracks/erlang/exercises/hamming/include/exercism.hrl +0 -11
  224. data/tracks/erlang/exercises/hello-world/include/exercism.hrl +0 -11
  225. data/tracks/erlang/exercises/isogram/include/exercism.hrl +0 -11
  226. data/tracks/erlang/exercises/largest-series-product/include/exercism.hrl +0 -11
  227. data/tracks/erlang/exercises/leap/include/exercism.hrl +0 -11
  228. data/tracks/erlang/exercises/luhn/include/exercism.hrl +0 -11
  229. data/tracks/erlang/exercises/meetup/include/exercism.hrl +0 -11
  230. data/tracks/erlang/exercises/nucleotide-count/include/exercism.hrl +0 -11
  231. data/tracks/erlang/exercises/parallel-letter-frequency/include/exercism.hrl +0 -11
  232. data/tracks/erlang/exercises/phone-number/include/exercism.hrl +0 -11
  233. data/tracks/erlang/exercises/rna-transcription/include/exercism.hrl +0 -11
  234. data/tracks/erlang/exercises/robot-simulator/include/exercism.hrl +0 -11
  235. data/tracks/erlang/exercises/roman-numerals/include/exercism.hrl +0 -11
  236. data/tracks/erlang/exercises/rotational-cipher/include/exercism.hrl +0 -11
  237. data/tracks/erlang/exercises/scrabble-score/include/exercism.hrl +0 -11
  238. data/tracks/erlang/exercises/series/include/exercism.hrl +0 -11
  239. data/tracks/erlang/exercises/sieve/include/exercism.hrl +0 -11
  240. data/tracks/erlang/exercises/space-age/include/exercism.hrl +0 -11
  241. data/tracks/erlang/exercises/spiral-matrix/include/exercism.hrl +0 -11
  242. data/tracks/erlang/exercises/strain/include/exercism.hrl +0 -11
  243. data/tracks/erlang/exercises/sum-of-multiples/include/exercism.hrl +0 -11
  244. data/tracks/erlang/exercises/triangle/include/exercism.hrl +0 -11
  245. data/tracks/erlang/exercises/two-fer/include/exercism.hrl +0 -11
  246. data/tracks/erlang/exercises/word-count/include/exercism.hrl +0 -11
  247. data/tracks/erlang/exercises/zipper/include/exercism.hrl +0 -11
@@ -0,0 +1,107 @@
1
+ # Book Store
2
+
3
+ To try and encourage more sales of different books from a popular 5 book
4
+ series, a bookshop has decided to offer discounts on multiple book purchases.
5
+
6
+ One copy of any of the five books costs $8.
7
+
8
+ If, however, you buy two different books, you get a 5%
9
+ discount on those two books.
10
+
11
+ If you buy 3 different books, you get a 10% discount.
12
+
13
+ If you buy 4 different books, you get a 20% discount.
14
+
15
+ If you buy all 5, you get a 25% discount.
16
+
17
+ Note: that if you buy four books, of which 3 are
18
+ different titles, you get a 10% discount on the 3 that
19
+ form part of a set, but the fourth book still costs $8.
20
+
21
+ Your mission is to write a piece of code to calculate the
22
+ price of any conceivable shopping basket (containing only
23
+ books of the same series), giving as big a discount as
24
+ possible.
25
+
26
+ For example, how much does this basket of books cost?
27
+
28
+ - 2 copies of the first book
29
+ - 2 copies of the second book
30
+ - 2 copies of the third book
31
+ - 1 copy of the fourth book
32
+ - 1 copy of the fifth book
33
+
34
+ One way of grouping these 8 books is:
35
+
36
+ - 1 group of 5 --> 25% discount (1st,2nd,3rd,4th,5th)
37
+ - +1 group of 3 --> 10% discount (1st,2nd,3rd)
38
+
39
+ This would give a total of:
40
+
41
+ - 5 books at a 25% discount
42
+ - +3 books at a 10% discount
43
+
44
+ Resulting in:
45
+
46
+ - 5 x (8 - 2.00) == 5 x 6.00 == $30.00
47
+ - +3 x (8 - 0.80) == 3 x 7.20 == $21.60
48
+
49
+ For a total of $51.60
50
+
51
+ However, a different way to group these 8 books is:
52
+
53
+ - 1 group of 4 books --> 20% discount (1st,2nd,3rd,4th)
54
+ - +1 group of 4 books --> 20% discount (1st,2nd,3rd,5th)
55
+
56
+ This would give a total of:
57
+
58
+ - 4 books at a 20% discount
59
+ - +4 books at a 20% discount
60
+
61
+ Resulting in:
62
+
63
+ - 4 x (8 - 1.60) == 4 x 6.40 == $25.60
64
+ - +4 x (8 - 1.60) == 4 x 6.40 == $25.60
65
+
66
+ For a total of $51.20
67
+
68
+ And $51.20 is the price with the biggest discount.
69
+
70
+ ## Rust Installation
71
+
72
+ Refer to the [exercism help page][help-page] for Rust installation and learning
73
+ resources.
74
+
75
+ ## Writing the Code
76
+
77
+ Execute the tests with:
78
+
79
+ ```bash
80
+ $ cargo test
81
+ ```
82
+
83
+ All but the first test have been ignored. After you get the first test to
84
+ pass, remove the ignore flag (`#[ignore]`) from the next test and get the tests
85
+ to pass again. The test file is located in the `tests` directory. You can
86
+ also remove the ignore flag from all the tests to get them to run all at once
87
+ if you wish.
88
+
89
+ Make sure to read the [Modules](https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html) chapter if you
90
+ haven't already, it will help you with organizing your files.
91
+
92
+ ## Feedback, Issues, Pull Requests
93
+
94
+ The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the [rust track team](https://github.com/orgs/exercism/teams/rust) are happy to help!
95
+
96
+ If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md).
97
+
98
+ [help-page]: http://exercism.io/languages/rust
99
+ [modules]: https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html
100
+ [cargo]: https://doc.rust-lang.org/book/second-edition/ch14-00-more-about-cargo.html
101
+
102
+ ## Source
103
+
104
+ Inspired by the harry potter kata from Cyber-Dojo. [http://cyber-dojo.org](http://cyber-dojo.org)
105
+
106
+ ## Submitting Incomplete Solutions
107
+ It's possible to submit an incomplete solution so you can see how others have completed the exercise.
@@ -0,0 +1,187 @@
1
+ use std::cmp::Ordering;
2
+ use std::collections::{BTreeSet, HashSet};
3
+ use std::collections::hash_map::DefaultHasher;
4
+ use std::hash::{Hash, Hasher};
5
+ use std::mem;
6
+ use std::cell::RefCell;
7
+
8
+ extern crate noisy_float;
9
+ use noisy_float::prelude::*;
10
+
11
+ type Book = usize;
12
+ type GroupedBasket = Vec<Group>;
13
+ type Price = f64;
14
+ const BOOK_PRICE: Price = 8.0;
15
+
16
+ #[derive(Debug, Clone, PartialEq, Eq)]
17
+ struct Group(RefCell<BTreeSet<Book>>);
18
+
19
+ impl Group {
20
+ fn new() -> Group {
21
+ Group(RefCell::new(BTreeSet::new()))
22
+ }
23
+
24
+ fn new_containing(book: Book) -> Group {
25
+ let g = Group::new();
26
+ g.0.borrow_mut().insert(book);
27
+ g
28
+ }
29
+
30
+ fn price(&self) -> Price {
31
+ (self.0.borrow().len() as Price) * BOOK_PRICE *
32
+ match self.0.borrow().len() {
33
+ 2 => 0.95,
34
+ 3 => 0.90,
35
+ 4 => 0.80,
36
+ 5 => 0.75,
37
+ _ => 1.0,
38
+ }
39
+ }
40
+ }
41
+
42
+
43
+ impl Ord for Group {
44
+ // we want to order groups first by qty contained DESC, then by lowest value ASC
45
+ fn cmp(&self, other: &Group) -> Ordering {
46
+ match other.0.borrow().len().cmp(&self.0.borrow().len()) {
47
+ Ordering::Equal => {
48
+ if self.0.borrow().len() == 0 {
49
+ Ordering::Equal
50
+ } else {
51
+ self.0.borrow().iter().next().unwrap().cmp(
52
+ other
53
+ .0
54
+ .borrow()
55
+ .iter()
56
+ .next()
57
+ .unwrap(),
58
+ )
59
+ }
60
+ }
61
+ otherwise => otherwise,
62
+ }
63
+ }
64
+ }
65
+
66
+ impl PartialOrd for Group {
67
+ fn partial_cmp(&self, other: &Group) -> Option<Ordering> {
68
+ Some(self.cmp(other))
69
+ }
70
+ }
71
+
72
+ impl Hash for Group {
73
+ fn hash<H: Hasher>(&self, hasher: &mut H) {
74
+ self.0.borrow().hash(hasher);
75
+ }
76
+ }
77
+
78
+ fn basket_price(basket: &GroupedBasket) -> Price {
79
+ basket.iter().map(|g| g.price()).sum()
80
+ }
81
+
82
+ /// Compute the hash of a GroupedBasket
83
+ ///
84
+ /// Note that we don't actually care at all about the _values_ within
85
+ /// the groups, only their lengths. Therefore, let's hash not the actual
86
+ /// GB but its lengths.
87
+ fn hash_of(basket: &GroupedBasket) -> u64 {
88
+ let lengths = basket
89
+ .iter()
90
+ .map(|g| g.0.borrow().len())
91
+ .collect::<Vec<_>>();
92
+ let mut hasher = DefaultHasher::new();
93
+ lengths.hash(&mut hasher);
94
+ hasher.finish()
95
+ }
96
+
97
+ pub fn lowest_price(books: &[Book]) -> Price {
98
+ DecomposeGroups::new(books)
99
+ .map(|gb| r64(basket_price(&gb)))
100
+ .min()
101
+ .map(|r| r.raw())
102
+ .unwrap_or(0.0)
103
+ }
104
+
105
+ struct DecomposeGroups {
106
+ prev_states: HashSet<u64>,
107
+ next: Option<GroupedBasket>,
108
+ }
109
+
110
+ impl Iterator for DecomposeGroups {
111
+ type Item = GroupedBasket;
112
+ fn next(&mut self) -> Option<Self::Item> {
113
+ // our goal here: produce a stream of valid groups, differentiated by their
114
+ // counts, from most compact to most dispersed.
115
+ //
116
+ // Algorithm:
117
+ // - Start with the most compact groups possible
118
+ // - If the number of groups == 0 or the max population of any group == 1, return None
119
+ // - For every item in the most populous group:
120
+ // - Try removing it and adding it to a smaller group.
121
+ // - Can any smaller group accept it? if yes, move it there and return
122
+ // - If it cannot be added to any smaller group, try the next item from this set
123
+ // - If no item from the most populous group can be added to any smaller group,
124
+ // then move the last item from the most populous group into a new group, alone,
125
+ // and return
126
+ let return_value = self.next.clone();
127
+ if let Some(groups) = mem::replace(&mut self.next, None) {
128
+ if !(groups.is_empty() || groups.iter().all(|g| g.0.borrow().len() == 1)) {
129
+ let mut hypothetical;
130
+ for mpg_book in groups[0].0.borrow().iter() {
131
+ for (idx, other_group) in groups[1..].iter().enumerate() {
132
+ if !other_group.0.borrow().contains(mpg_book) {
133
+ hypothetical = groups.clone();
134
+ hypothetical[0].0.borrow_mut().remove(mpg_book);
135
+ hypothetical[1 + idx].0.borrow_mut().insert(*mpg_book);
136
+ hypothetical.sort();
137
+ let hypothetical_hash = hash_of(&hypothetical);
138
+ if !self.prev_states.contains(&hypothetical_hash) {
139
+ self.prev_states.insert(hypothetical_hash);
140
+ mem::replace(&mut self.next, Some(hypothetical));
141
+ return return_value;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ // we've gone through all the items of the most populous group,
147
+ // and none of them can be added to any other existing group.
148
+ // We need to create a new group;
149
+ let book = {
150
+ let backing_bt = groups[0].0.borrow();
151
+ let mut book_iter = backing_bt.iter();
152
+ book_iter.next().unwrap().clone()
153
+ };
154
+ hypothetical = groups.clone();
155
+ hypothetical[0].0.borrow_mut().remove(&book);
156
+ hypothetical.push(Group::new_containing(book));
157
+ hypothetical.sort();
158
+ self.prev_states.insert(hash_of(&hypothetical));
159
+ mem::replace(&mut self.next, Some(hypothetical));
160
+ }
161
+ }
162
+ return_value
163
+ }
164
+ }
165
+
166
+ impl DecomposeGroups {
167
+ fn new(books: &[Book]) -> DecomposeGroups {
168
+ let mut book_groups = GroupedBasket::new();
169
+ 'nextbook: for book in books {
170
+ for idx in 0..book_groups.len() {
171
+ if !book_groups[idx].0.borrow().contains(&book) {
172
+ book_groups[idx].0.borrow_mut().insert(*book);
173
+ continue 'nextbook;
174
+ }
175
+ }
176
+ // if we're here, we still haven't found a place for the book.
177
+ // better add it to a new group
178
+ book_groups.push(Group::new_containing(*book));
179
+ }
180
+ book_groups.sort();
181
+
182
+ DecomposeGroups {
183
+ next: Some(book_groups),
184
+ prev_states: HashSet::new(),
185
+ }
186
+ }
187
+ }
@@ -0,0 +1,3 @@
1
+ pub fn lowest_price(_: &[usize]) -> f64 {
2
+ unimplemented!()
3
+ }
@@ -0,0 +1,130 @@
1
+ //! Tests for book-store
2
+ //!
3
+ //! Generated by [script][script] using [canonical data][canonical-data]
4
+ //!
5
+ //! [script]: https://github.com/exercism/rust/blob/master/bin/init_exercise.py
6
+ //! [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/book-store/canonical_data.json
7
+
8
+ extern crate book_store;
9
+ use book_store::*;
10
+
11
+ /// Process a single test case for the property `total`
12
+ ///
13
+ /// All cases for the `total` property are implemented
14
+ /// in terms of this function.
15
+ ///
16
+ /// Expected input format: ('basket', 'targetgrouping')
17
+ fn process_total_case(input: (Vec<usize>, Vec<Vec<usize>>), expected: f64) {
18
+ assert_eq!(
19
+ lowest_price(&input.0),
20
+ expected
21
+ )
22
+ }
23
+
24
+ // Return the total basket price after applying the best discount.
25
+ // Calculate lowest price for a shopping basket containing books only from
26
+ // a single series. There is no discount advantage for having more than
27
+ // one copy of any single book in a grouping.
28
+
29
+
30
+ #[test]
31
+ /// Only a single book
32
+ fn test_only_a_single_book() {
33
+ process_total_case((vec![1], vec![vec![1]]), 8.0);
34
+ }
35
+
36
+
37
+ #[test]
38
+ #[ignore]
39
+ /// Two of the same book
40
+ fn test_two_of_the_same_book() {
41
+ process_total_case((vec![2, 2], vec![vec![2], vec![2]]), 16.0);
42
+ }
43
+
44
+
45
+ #[test]
46
+ #[ignore]
47
+ /// Empty basket
48
+ fn test_empty_basket() {
49
+ process_total_case((vec![], vec![]), 0.0);
50
+ }
51
+
52
+
53
+ #[test]
54
+ #[ignore]
55
+ /// Two different books
56
+ fn test_two_different_books() {
57
+ process_total_case((vec![1, 2], vec![vec![1, 2]]), 15.2);
58
+ }
59
+
60
+
61
+ #[test]
62
+ #[ignore]
63
+ /// Three different books
64
+ fn test_three_different_books() {
65
+ process_total_case((vec![1, 2, 3], vec![vec![1, 2, 3]]), 21.6);
66
+ }
67
+
68
+
69
+ #[test]
70
+ #[ignore]
71
+ /// Four different books
72
+ fn test_four_different_books() {
73
+ process_total_case((vec![1, 2, 3, 4], vec![vec![1, 2, 3, 4]]), 25.6);
74
+ }
75
+
76
+
77
+ #[test]
78
+ #[ignore]
79
+ /// Five different books
80
+ fn test_five_different_books() {
81
+ process_total_case((vec![1, 2, 3, 4, 5], vec![vec![1, 2, 3, 4, 5]]), 30.0);
82
+ }
83
+
84
+
85
+ #[test]
86
+ #[ignore]
87
+ /// Two groups of four is cheaper than group of five plus group of three
88
+ fn test_two_groups_of_four_is_cheaper_than_group_of_five_plus_group_of_three() {
89
+ process_total_case((vec![1, 1, 2, 2, 3, 3, 4, 5], vec![vec![1, 2, 3, 4], vec![1, 2, 3, 5]]), 51.2);
90
+ }
91
+
92
+
93
+ #[test]
94
+ #[ignore]
95
+ /// Group of four plus group of two is cheaper than two groups of three
96
+ fn test_group_of_four_plus_group_of_two_is_cheaper_than_two_groups_of_three() {
97
+ process_total_case((vec![1, 1, 2, 2, 3, 4], vec![vec![1, 2, 3, 4], vec![1, 2]]), 40.8);
98
+ }
99
+
100
+
101
+ #[test]
102
+ #[ignore]
103
+ /// Two each of first 4 books and 1 copy each of rest
104
+ fn test_two_each_of_first_4_books_and_1_copy_each_of_rest() {
105
+ process_total_case((vec![1, 1, 2, 2, 3, 3, 4, 4, 5], vec![vec![1, 2, 3, 4, 5], vec![1, 2, 3, 4]]), 55.6);
106
+ }
107
+
108
+
109
+ #[test]
110
+ #[ignore]
111
+ /// Two copies of each book
112
+ fn test_two_copies_of_each_book() {
113
+ process_total_case((vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5], vec![vec![1, 2, 3, 4, 5], vec![1, 2, 3, 4, 5]]), 60.0);
114
+ }
115
+
116
+
117
+ #[test]
118
+ #[ignore]
119
+ /// Three copies of first book and 2 each of remaining
120
+ fn test_three_copies_of_first_book_and_2_each_of_remaining() {
121
+ process_total_case((vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1], vec![vec![1, 2, 3, 4, 5], vec![1, 2, 3, 4, 5], vec![1]]), 68.0);
122
+ }
123
+
124
+
125
+ #[test]
126
+ #[ignore]
127
+ /// Three each of first 2 books and 2 each of remaining books
128
+ fn test_three_each_of_first_2_books_and_2_each_of_remaining_books() {
129
+ process_total_case((vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2], vec![vec![1, 2, 3, 4, 5], vec![1, 2, 3, 4, 5], vec![1, 2]]), 75.2);
130
+ }
@@ -6,7 +6,7 @@
6
6
  "test_pattern": "test[.]sml$",
7
7
  "exercises": [
8
8
  {
9
- "uuid": "4450455d-0786-6f80-7672-48368a115df16e0aa7e",
9
+ "uuid": "a00f98a6-98dd-4c8a-b3d1-adc1d56eef29",
10
10
  "slug": "hello-world",
11
11
  "core": true,
12
12
  "unlocked_by": null,
@@ -46,7 +46,7 @@
46
46
  ]
47
47
  },
48
48
  {
49
- "uuid": "9d8e76f1-0e85-a280-b779-16335f29d1a96ce3a72",
49
+ "uuid": "4fb40e77-727b-41bc-ac15-b909a26bb917",
50
50
  "slug": "bob",
51
51
  "core": false,
52
52
  "unlocked_by": null,
@@ -126,7 +126,7 @@
126
126
  ]
127
127
  },
128
128
  {
129
- "uuid": "b6713a74-08d5-4480-6524-f9dbf5cf2563d2b2a91",
129
+ "uuid": "580496d5-bec7-401d-b0cf-dc16da795491",
130
130
  "slug": "difference-of-squares",
131
131
  "core": false,
132
132
  "unlocked_by": null,
@@ -136,7 +136,7 @@
136
136
  ]
137
137
  },
138
138
  {
139
- "uuid": "2bfc685e-0d13-0280-de5e-53bc093e3a8c5ac7c73",
139
+ "uuid": "0f488d4b-89da-4d1f-958b-a7b3171f43d5",
140
140
  "slug": "atbash-cipher",
141
141
  "core": false,
142
142
  "unlocked_by": null,
@@ -146,7 +146,7 @@
146
146
  ]
147
147
  },
148
148
  {
149
- "uuid": "b83ce171-06e6-dd80-080e-9dd189bf6df83833e92",
149
+ "uuid": "8ccc61c6-fac5-4af7-803e-d0f6d10f8a1f",
150
150
  "slug": "perfect-numbers",
151
151
  "core": false,
152
152
  "unlocked_by": null,
@@ -156,7 +156,7 @@
156
156
  ]
157
157
  },
158
158
  {
159
- "uuid": "42adb6ed-09f0-6980-2d4f-75ac90cc2bf1f59fb3a",
159
+ "uuid": "d17ab3c6-82a1-413e-9018-8775ec9ea498",
160
160
  "slug": "pangram",
161
161
  "core": false,
162
162
  "unlocked_by": null,
@@ -166,7 +166,7 @@
166
166
  ]
167
167
  },
168
168
  {
169
- "uuid": "6c191163-0a98-2380-debc-75144c1787f3853e868",
169
+ "uuid": "e8ed5a0f-796c-4a1b-af7a-2502254d79a8",
170
170
  "slug": "prime-factors",
171
171
  "core": false,
172
172
  "unlocked_by": null,
@@ -206,7 +206,7 @@
206
206
  ]
207
207
  },
208
208
  {
209
- "uuid": "b9377be8-06c1-5280-33b6-a2071298947c9ddf65e",
209
+ "uuid": "4bce27a8-2b91-4595-b6e6-d4be7e704113",
210
210
  "slug": "sum-of-multiples",
211
211
  "core": false,
212
212
  "unlocked_by": null,
@@ -157,7 +157,7 @@
157
157
  "recursion"
158
158
  ],
159
159
  "unlocked_by": "sum-of-multiples",
160
- "uuid": "b2f90523-0cb1-f080-a03c-5aee5f919abf93b491b"
160
+ "uuid": "b912fe4f-c505-4bac-8029-76412644375b"
161
161
  },
162
162
  {
163
163
  "core": true,
@@ -413,7 +413,7 @@
413
413
  "mathematics"
414
414
  ],
415
415
  "unlocked_by": "leap",
416
- "uuid": "cf2d545c-036e-0980-3ac2-64ad54c3867e37f4cfa"
416
+ "uuid": "0eb37e93-d191-4451-bd77-cd3681c91d94"
417
417
  },
418
418
  {
419
419
  "core": true,
@@ -424,7 +424,7 @@
424
424
  "control_flow_loops",
425
425
  "mathematics"
426
426
  ],
427
- "uuid": "a4054ca4-0737-6e80-3ca5-372be502b1d8bb93b20"
427
+ "uuid": "10edb37d-cf5d-443a-bb4c-8831a3986b61"
428
428
  },
429
429
  {
430
430
  "core": false,
@@ -436,7 +436,7 @@
436
436
  "control_flow_conditionals",
437
437
  "control_flow_loops"
438
438
  ],
439
- "uuid": "b2fcd8f0-03b2-7880-ec70-65ad5cedfdd8116012d"
439
+ "uuid": "53584e8d-9b8d-4c0e-8ad8-4c228fcf6bcf"
440
440
  },
441
441
  {
442
442
  "core": false,
@@ -447,7 +447,7 @@
447
447
  "iterators",
448
448
  "mathematics"
449
449
  ],
450
- "uuid": "1447f7bc-0d30-b980-378b-ab8defd8ff148419dbd"
450
+ "uuid": "dbe39983-5635-4369-89a3-fd549144259b"
451
451
  },
452
452
  {
453
453
  "core": false,
@@ -511,7 +511,7 @@
511
511
  "uuid": "61f0964f-0779-446d-bd6b-6bb988414302"
512
512
  },
513
513
  {
514
- "uuid": "2331a447-054d-4a80-52d7-fe2d667baaafcd3fc58",
514
+ "uuid": "df7c1bff-2224-422b-9c34-64b28b09510e",
515
515
  "slug": "diamond",
516
516
  "core": false,
517
517
  "unlocked_by": "pascals-triangle",