From 7c07232c158c77386914547b661e4875bb659f55 Mon Sep 17 00:00:00 2001 From: Mohamed Hernouf Date: Mon, 5 Jun 2023 15:28:16 +0200 Subject: [PATCH 1/8] =?UTF-8?q?Adding=20exercises=20by=20Hugoo=20Fer=C3=A9?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exercises/hferee/1.2_declarations/descr.md | 27 +++ exercises/hferee/1.2_declarations/meta.json | 7 + exercises/hferee/1.2_declarations/prelude.ml | 0 exercises/hferee/1.2_declarations/prepare.ml | 1 + exercises/hferee/1.2_declarations/solution.ml | 8 + exercises/hferee/1.2_declarations/template.ml | 12 ++ exercises/hferee/1.2_declarations/test.ml | 39 +++++ exercises/hferee/1.3_bool/descr.md | 71 ++++++++ exercises/hferee/1.3_bool/meta.json | 7 + exercises/hferee/1.3_bool/prelude.ml | 0 exercises/hferee/1.3_bool/prepare.ml | 0 exercises/hferee/1.3_bool/solution.ml | 24 +++ exercises/hferee/1.3_bool/template.ml | 24 +++ exercises/hferee/1.3_bool/test.ml | 63 +++++++ exercises/hferee/1.4_conditionals/descr.md | 73 ++++++++ exercises/hferee/1.4_conditionals/meta.json | 7 + exercises/hferee/1.4_conditionals/prelude.ml | 1 + exercises/hferee/1.4_conditionals/prepare.ml | 1 + exercises/hferee/1.4_conditionals/solution.ml | 34 ++++ exercises/hferee/1.4_conditionals/template.ml | 22 +++ exercises/hferee/1.4_conditionals/test.ml | 82 +++++++++ exercises/hferee/10_assoc/descr.md | 55 ++++++ exercises/hferee/10_assoc/meta.json | 7 + exercises/hferee/10_assoc/prelude.ml | 1 + exercises/hferee/10_assoc/prepare.ml | 1 + exercises/hferee/10_assoc/solution.ml | 67 ++++++++ exercises/hferee/10_assoc/template.ml | 1 + exercises/hferee/10_assoc/test.ml | 91 ++++++++++ .../hferee/10_parameterized_lists/descr.md | 32 ++++ .../hferee/10_parameterized_lists/meta.json | 7 + .../hferee/10_parameterized_lists/prelude.ml | 1 + .../hferee/10_parameterized_lists/prepare.ml | 0 .../hferee/10_parameterized_lists/solution.ml | 12 ++ .../hferee/10_parameterized_lists/template.ml | 1 + .../hferee/10_parameterized_lists/test.ml | 59 +++++++ exercises/hferee/10_run_length/descr.md | 39 +++++ exercises/hferee/10_run_length/meta.json | 7 + exercises/hferee/10_run_length/prelude.ml | 1 + exercises/hferee/10_run_length/prepare.ml | 0 exercises/hferee/10_run_length/solution.ml | 35 ++++ exercises/hferee/10_run_length/template.ml | 1 + exercises/hferee/10_run_length/test.ml | 32 ++++ exercises/hferee/11_lists_lists/descr.md | 21 +++ exercises/hferee/11_lists_lists/meta.json | 7 + exercises/hferee/11_lists_lists/prelude.ml | 1 + exercises/hferee/11_lists_lists/prepare.ml | 0 exercises/hferee/11_lists_lists/solution.ml | 55 ++++++ exercises/hferee/11_lists_lists/template.ml | 1 + exercises/hferee/11_lists_lists/test.ml | 43 +++++ exercises/hferee/11_printable/descr.md | 30 ++++ exercises/hferee/11_printable/meta.json | 7 + exercises/hferee/11_printable/prelude.ml | 1 + exercises/hferee/11_printable/prepare.ml | 0 exercises/hferee/11_printable/solution.ml | 27 +++ exercises/hferee/11_printable/template.ml | 1 + exercises/hferee/11_printable/test.ml | 30 ++++ exercises/hferee/3.0_sudoku/descr.md | 161 ++++++++++++++++++ exercises/hferee/3.0_sudoku/meta.json | 6 + exercises/hferee/3.0_sudoku/prelude.ml | 32 ++++ exercises/hferee/3.0_sudoku/prepare.ml | 0 exercises/hferee/3.0_sudoku/solution.ml | 134 +++++++++++++++ exercises/hferee/3.0_sudoku/template.ml | 2 + exercises/hferee/3.0_sudoku/test.ml | 147 ++++++++++++++++ .../hferee/4.0_rock_paper_scissors/descr.md | 47 +++++ .../hferee/4.0_rock_paper_scissors/meta.json | 7 + .../hferee/4.0_rock_paper_scissors/prelude.ml | 5 + .../hferee/4.0_rock_paper_scissors/prepare.ml | 6 + .../4.0_rock_paper_scissors/solution.ml | 19 +++ .../4.0_rock_paper_scissors/template.ml | 1 + .../hferee/4.0_rock_paper_scissors/test.ml | 34 ++++ exercises/hferee/4.2_coordinates/descr.md | 27 +++ exercises/hferee/4.2_coordinates/meta.json | 7 + exercises/hferee/4.2_coordinates/prelude.ml | 5 + exercises/hferee/4.2_coordinates/prepare.ml | 0 exercises/hferee/4.2_coordinates/solution.ml | 11 ++ exercises/hferee/4.2_coordinates/template.ml | 1 + exercises/hferee/4.2_coordinates/test.ml | 13 ++ exercises/hferee/5.0_prime_numbers/descr.md | 27 +++ exercises/hferee/5.0_prime_numbers/meta.json | 7 + exercises/hferee/5.0_prime_numbers/prelude.ml | 1 + exercises/hferee/5.0_prime_numbers/prepare.ml | 0 .../hferee/5.0_prime_numbers/solution.ml | 17 ++ .../hferee/5.0_prime_numbers/template.ml | 0 exercises/hferee/5.0_prime_numbers/test.ml | 39 +++++ exercises/hferee/5.1_DNA/descr.md | 34 ++++ exercises/hferee/5.1_DNA/meta.json | 7 + exercises/hferee/5.1_DNA/prelude.ml | 1 + exercises/hferee/5.1_DNA/prepare.ml | 0 exercises/hferee/5.1_DNA/solution.ml | 36 ++++ exercises/hferee/5.1_DNA/template.ml | 1 + exercises/hferee/5.1_DNA/test.ml | 48 ++++++ exercises/hferee/5.2_sierpinski_vg/descr.md | 41 +++++ .../5.2_sierpinski_vg/images/sierpinsky6.png | Bin 0 -> 18684 bytes exercises/hferee/5.2_sierpinski_vg/meta.json | 7 + exercises/hferee/5.2_sierpinski_vg/prelude.ml | 1 + exercises/hferee/5.2_sierpinski_vg/prepare.ml | 18 ++ .../hferee/5.2_sierpinski_vg/solution.ml | 15 ++ .../hferee/5.2_sierpinski_vg/template.ml | 1 + exercises/hferee/5.2_sierpinski_vg/test.ml | 25 +++ exercises/hferee/6_hanoi/descr.md | 48 ++++++ exercises/hferee/6_hanoi/images/hanoi.gif | Bin 0 -> 166746 bytes exercises/hferee/6_hanoi/images/hanoi_big.gif | Bin 0 -> 253148 bytes exercises/hferee/6_hanoi/meta.json | 7 + exercises/hferee/6_hanoi/prelude.ml | 1 + exercises/hferee/6_hanoi/prepare.ml | 0 exercises/hferee/6_hanoi/solution.ml | 24 +++ exercises/hferee/6_hanoi/template.ml | 1 + exercises/hferee/6_hanoi/test.ml | 32 ++++ exercises/hferee/6_sierpinsky_ascii/descr.md | 71 ++++++++ exercises/hferee/6_sierpinsky_ascii/meta.json | 7 + .../hferee/6_sierpinsky_ascii/prelude.ml | 1 + .../hferee/6_sierpinsky_ascii/prepare.ml | 0 .../hferee/6_sierpinsky_ascii/solution.ml | 26 +++ .../hferee/6_sierpinsky_ascii/template.ml | 1 + exercises/hferee/6_sierpinsky_ascii/test.ml | 52 ++++++ exercises/hferee/7_lists/descr.md | 41 +++++ exercises/hferee/7_lists/meta.json | 7 + exercises/hferee/7_lists/prelude.ml | 1 + exercises/hferee/7_lists/prepare.ml | 0 exercises/hferee/7_lists/solution.ml | 56 ++++++ exercises/hferee/7_lists/template.ml | 1 + exercises/hferee/7_lists/test.ml | 62 +++++++ .../hferee/7_lists_binary_encoding/descr.md | 41 +++++ .../hferee/7_lists_binary_encoding/meta.json | 7 + .../hferee/7_lists_binary_encoding/prelude.ml | 1 + .../hferee/7_lists_binary_encoding/prepare.ml | 0 .../7_lists_binary_encoding/solution.ml | 33 ++++ .../7_lists_binary_encoding/template.ml | 1 + .../hferee/7_lists_binary_encoding/test.ml | 49 ++++++ exercises/hferee/8_fold/descr.md | 30 ++++ exercises/hferee/8_fold/meta.json | 7 + exercises/hferee/8_fold/prelude.ml | 5 + exercises/hferee/8_fold/prepare.ml | 0 exercises/hferee/8_fold/solution.ml | 22 +++ exercises/hferee/8_fold/template.ml | 0 exercises/hferee/8_fold/test.ml | 86 ++++++++++ exercises/hferee/8_sieve/descr.md | 23 +++ exercises/hferee/8_sieve/meta.json | 7 + exercises/hferee/8_sieve/prelude.ml | 3 + exercises/hferee/8_sieve/prepare.ml | 0 exercises/hferee/8_sieve/solution.ml | 36 ++++ exercises/hferee/8_sieve/template.ml | 1 + exercises/hferee/8_sieve/test.ml | 32 ++++ exercises/hferee/8_sort/descr.md | 10 ++ exercises/hferee/8_sort/meta.json | 7 + exercises/hferee/8_sort/prelude.ml | 6 + exercises/hferee/8_sort/prepare.ml | 0 exercises/hferee/8_sort/solution.ml | 13 ++ exercises/hferee/8_sort/template.ml | 1 + exercises/hferee/8_sort/test.ml | 48 ++++++ 150 files changed, 3076 insertions(+) create mode 100644 exercises/hferee/1.2_declarations/descr.md create mode 100644 exercises/hferee/1.2_declarations/meta.json create mode 100644 exercises/hferee/1.2_declarations/prelude.ml create mode 100644 exercises/hferee/1.2_declarations/prepare.ml create mode 100644 exercises/hferee/1.2_declarations/solution.ml create mode 100644 exercises/hferee/1.2_declarations/template.ml create mode 100644 exercises/hferee/1.2_declarations/test.ml create mode 100644 exercises/hferee/1.3_bool/descr.md create mode 100644 exercises/hferee/1.3_bool/meta.json create mode 100644 exercises/hferee/1.3_bool/prelude.ml create mode 100644 exercises/hferee/1.3_bool/prepare.ml create mode 100644 exercises/hferee/1.3_bool/solution.ml create mode 100644 exercises/hferee/1.3_bool/template.ml create mode 100644 exercises/hferee/1.3_bool/test.ml create mode 100644 exercises/hferee/1.4_conditionals/descr.md create mode 100644 exercises/hferee/1.4_conditionals/meta.json create mode 100644 exercises/hferee/1.4_conditionals/prelude.ml create mode 100644 exercises/hferee/1.4_conditionals/prepare.ml create mode 100644 exercises/hferee/1.4_conditionals/solution.ml create mode 100644 exercises/hferee/1.4_conditionals/template.ml create mode 100644 exercises/hferee/1.4_conditionals/test.ml create mode 100644 exercises/hferee/10_assoc/descr.md create mode 100644 exercises/hferee/10_assoc/meta.json create mode 100644 exercises/hferee/10_assoc/prelude.ml create mode 100644 exercises/hferee/10_assoc/prepare.ml create mode 100644 exercises/hferee/10_assoc/solution.ml create mode 100644 exercises/hferee/10_assoc/template.ml create mode 100644 exercises/hferee/10_assoc/test.ml create mode 100644 exercises/hferee/10_parameterized_lists/descr.md create mode 100644 exercises/hferee/10_parameterized_lists/meta.json create mode 100644 exercises/hferee/10_parameterized_lists/prelude.ml create mode 100644 exercises/hferee/10_parameterized_lists/prepare.ml create mode 100644 exercises/hferee/10_parameterized_lists/solution.ml create mode 100644 exercises/hferee/10_parameterized_lists/template.ml create mode 100644 exercises/hferee/10_parameterized_lists/test.ml create mode 100644 exercises/hferee/10_run_length/descr.md create mode 100644 exercises/hferee/10_run_length/meta.json create mode 100644 exercises/hferee/10_run_length/prelude.ml create mode 100644 exercises/hferee/10_run_length/prepare.ml create mode 100644 exercises/hferee/10_run_length/solution.ml create mode 100644 exercises/hferee/10_run_length/template.ml create mode 100644 exercises/hferee/10_run_length/test.ml create mode 100644 exercises/hferee/11_lists_lists/descr.md create mode 100644 exercises/hferee/11_lists_lists/meta.json create mode 100644 exercises/hferee/11_lists_lists/prelude.ml create mode 100644 exercises/hferee/11_lists_lists/prepare.ml create mode 100644 exercises/hferee/11_lists_lists/solution.ml create mode 100644 exercises/hferee/11_lists_lists/template.ml create mode 100644 exercises/hferee/11_lists_lists/test.ml create mode 100644 exercises/hferee/11_printable/descr.md create mode 100644 exercises/hferee/11_printable/meta.json create mode 100644 exercises/hferee/11_printable/prelude.ml create mode 100644 exercises/hferee/11_printable/prepare.ml create mode 100644 exercises/hferee/11_printable/solution.ml create mode 100644 exercises/hferee/11_printable/template.ml create mode 100644 exercises/hferee/11_printable/test.ml create mode 100644 exercises/hferee/3.0_sudoku/descr.md create mode 100644 exercises/hferee/3.0_sudoku/meta.json create mode 100644 exercises/hferee/3.0_sudoku/prelude.ml create mode 100644 exercises/hferee/3.0_sudoku/prepare.ml create mode 100644 exercises/hferee/3.0_sudoku/solution.ml create mode 100644 exercises/hferee/3.0_sudoku/template.ml create mode 100644 exercises/hferee/3.0_sudoku/test.ml create mode 100644 exercises/hferee/4.0_rock_paper_scissors/descr.md create mode 100644 exercises/hferee/4.0_rock_paper_scissors/meta.json create mode 100644 exercises/hferee/4.0_rock_paper_scissors/prelude.ml create mode 100644 exercises/hferee/4.0_rock_paper_scissors/prepare.ml create mode 100644 exercises/hferee/4.0_rock_paper_scissors/solution.ml create mode 100644 exercises/hferee/4.0_rock_paper_scissors/template.ml create mode 100644 exercises/hferee/4.0_rock_paper_scissors/test.ml create mode 100644 exercises/hferee/4.2_coordinates/descr.md create mode 100644 exercises/hferee/4.2_coordinates/meta.json create mode 100644 exercises/hferee/4.2_coordinates/prelude.ml create mode 100644 exercises/hferee/4.2_coordinates/prepare.ml create mode 100644 exercises/hferee/4.2_coordinates/solution.ml create mode 100644 exercises/hferee/4.2_coordinates/template.ml create mode 100644 exercises/hferee/4.2_coordinates/test.ml create mode 100644 exercises/hferee/5.0_prime_numbers/descr.md create mode 100644 exercises/hferee/5.0_prime_numbers/meta.json create mode 100644 exercises/hferee/5.0_prime_numbers/prelude.ml create mode 100644 exercises/hferee/5.0_prime_numbers/prepare.ml create mode 100644 exercises/hferee/5.0_prime_numbers/solution.ml create mode 100644 exercises/hferee/5.0_prime_numbers/template.ml create mode 100644 exercises/hferee/5.0_prime_numbers/test.ml create mode 100644 exercises/hferee/5.1_DNA/descr.md create mode 100644 exercises/hferee/5.1_DNA/meta.json create mode 100644 exercises/hferee/5.1_DNA/prelude.ml create mode 100644 exercises/hferee/5.1_DNA/prepare.ml create mode 100644 exercises/hferee/5.1_DNA/solution.ml create mode 100644 exercises/hferee/5.1_DNA/template.ml create mode 100644 exercises/hferee/5.1_DNA/test.ml create mode 100644 exercises/hferee/5.2_sierpinski_vg/descr.md create mode 100644 exercises/hferee/5.2_sierpinski_vg/images/sierpinsky6.png create mode 100644 exercises/hferee/5.2_sierpinski_vg/meta.json create mode 100644 exercises/hferee/5.2_sierpinski_vg/prelude.ml create mode 100644 exercises/hferee/5.2_sierpinski_vg/prepare.ml create mode 100644 exercises/hferee/5.2_sierpinski_vg/solution.ml create mode 100644 exercises/hferee/5.2_sierpinski_vg/template.ml create mode 100644 exercises/hferee/5.2_sierpinski_vg/test.ml create mode 100644 exercises/hferee/6_hanoi/descr.md create mode 100644 exercises/hferee/6_hanoi/images/hanoi.gif create mode 100644 exercises/hferee/6_hanoi/images/hanoi_big.gif create mode 100644 exercises/hferee/6_hanoi/meta.json create mode 100644 exercises/hferee/6_hanoi/prelude.ml create mode 100644 exercises/hferee/6_hanoi/prepare.ml create mode 100644 exercises/hferee/6_hanoi/solution.ml create mode 100644 exercises/hferee/6_hanoi/template.ml create mode 100644 exercises/hferee/6_hanoi/test.ml create mode 100644 exercises/hferee/6_sierpinsky_ascii/descr.md create mode 100644 exercises/hferee/6_sierpinsky_ascii/meta.json create mode 100644 exercises/hferee/6_sierpinsky_ascii/prelude.ml create mode 100644 exercises/hferee/6_sierpinsky_ascii/prepare.ml create mode 100644 exercises/hferee/6_sierpinsky_ascii/solution.ml create mode 100644 exercises/hferee/6_sierpinsky_ascii/template.ml create mode 100644 exercises/hferee/6_sierpinsky_ascii/test.ml create mode 100644 exercises/hferee/7_lists/descr.md create mode 100644 exercises/hferee/7_lists/meta.json create mode 100644 exercises/hferee/7_lists/prelude.ml create mode 100644 exercises/hferee/7_lists/prepare.ml create mode 100644 exercises/hferee/7_lists/solution.ml create mode 100644 exercises/hferee/7_lists/template.ml create mode 100644 exercises/hferee/7_lists/test.ml create mode 100644 exercises/hferee/7_lists_binary_encoding/descr.md create mode 100644 exercises/hferee/7_lists_binary_encoding/meta.json create mode 100644 exercises/hferee/7_lists_binary_encoding/prelude.ml create mode 100644 exercises/hferee/7_lists_binary_encoding/prepare.ml create mode 100644 exercises/hferee/7_lists_binary_encoding/solution.ml create mode 100644 exercises/hferee/7_lists_binary_encoding/template.ml create mode 100644 exercises/hferee/7_lists_binary_encoding/test.ml create mode 100644 exercises/hferee/8_fold/descr.md create mode 100644 exercises/hferee/8_fold/meta.json create mode 100644 exercises/hferee/8_fold/prelude.ml create mode 100644 exercises/hferee/8_fold/prepare.ml create mode 100644 exercises/hferee/8_fold/solution.ml create mode 100644 exercises/hferee/8_fold/template.ml create mode 100644 exercises/hferee/8_fold/test.ml create mode 100644 exercises/hferee/8_sieve/descr.md create mode 100644 exercises/hferee/8_sieve/meta.json create mode 100644 exercises/hferee/8_sieve/prelude.ml create mode 100644 exercises/hferee/8_sieve/prepare.ml create mode 100644 exercises/hferee/8_sieve/solution.ml create mode 100644 exercises/hferee/8_sieve/template.ml create mode 100644 exercises/hferee/8_sieve/test.ml create mode 100644 exercises/hferee/8_sort/descr.md create mode 100644 exercises/hferee/8_sort/meta.json create mode 100644 exercises/hferee/8_sort/prelude.ml create mode 100644 exercises/hferee/8_sort/prepare.ml create mode 100644 exercises/hferee/8_sort/solution.ml create mode 100644 exercises/hferee/8_sort/template.ml create mode 100644 exercises/hferee/8_sort/test.ml diff --git a/exercises/hferee/1.2_declarations/descr.md b/exercises/hferee/1.2_declarations/descr.md new file mode 100644 index 0000000..7a62000 --- /dev/null +++ b/exercises/hferee/1.2_declarations/descr.md @@ -0,0 +1,27 @@ +**Question :** For each of the OCaml phrases below, predict the calculated +result value by OCaml for that phrase and indicate this value on the left +instead of the corresponding -1. Then, verify your answers using automatic +notation (click the "Grade!" button above). + +```ocaml + +let phrase0 = + let x = 3 + in x + 1 + +let phrase1 = + let x = 3 in + let y = x + 1 + in x + y + +let phrase2 = + let x = 2 in + let x = x + x + in x + x + +(* notice the "and" syntax and guess what it means *) +let phrase3 = + let x = 2 in + let x = 3 and y = x + 1 + in x + y +``` diff --git a/exercises/hferee/1.2_declarations/meta.json b/exercises/hferee/1.2_declarations/meta.json new file mode 100644 index 0000000..132c694 --- /dev/null +++ b/exercises/hferee/1.2_declarations/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Local declarations", + "backward_exercises": ["mooc/week1/seq3/ex1"] +} diff --git a/exercises/hferee/1.2_declarations/prelude.ml b/exercises/hferee/1.2_declarations/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/1.2_declarations/prepare.ml b/exercises/hferee/1.2_declarations/prepare.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/1.2_declarations/prepare.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/1.2_declarations/solution.ml b/exercises/hferee/1.2_declarations/solution.ml new file mode 100644 index 0000000..5afb7ff --- /dev/null +++ b/exercises/hferee/1.2_declarations/solution.ml @@ -0,0 +1,8 @@ +let phrase0 = 4 +let phrase1 = 7 +let phrase2 = 8 +let phrase3 = 6 +let phrase4 = + let x = 7 * 7 in + let x = x * x in + x * x diff --git a/exercises/hferee/1.2_declarations/template.ml b/exercises/hferee/1.2_declarations/template.ml new file mode 100644 index 0000000..75e02e2 --- /dev/null +++ b/exercises/hferee/1.2_declarations/template.ml @@ -0,0 +1,12 @@ +(* Remplacer les -1 par vos réponses *) + +let phrase0 = -1 + +let phrase1 = -1 + +let phrase2 = -1 + +let phrase3 = -1 + +let phrase4 = -1 + diff --git a/exercises/hferee/1.2_declarations/test.ml b/exercises/hferee/1.2_declarations/test.ml new file mode 100644 index 0000000..27b4fa6 --- /dev/null +++ b/exercises/hferee/1.2_declarations/test.ml @@ -0,0 +1,39 @@ +open Test_lib +open Report + +let testint name = + Section ([ Code name ], test_variable_against_solution [%ty : int] name) + +(* checks that the number of occurrences of function f in code_ast is n *) +let check_count_fun code_ast name f n = + let count = ref 0 in + let reports = find_binding code_ast name (ast_check_expr ~on_variable_occurence: + (fun v -> if v = f then incr count; [])) + in + if !count <> n then [Message ([Text ("Il faut utiliser exactement " ^ string_of_int n ^ " fois '" ^ f ^ "'.")], Failure)] + else [Message ([Text("Bon nombre de '" ^ f ^ "' : " ^ string_of_int n ^ ".")], Success 1)] + + +let testmult name = + Section ([Code name], + test_variable_against_solution [%ty : int] name @ + check_count_fun code_ast name "*" 3) + +let testadd name = + Section ([Code name], + test_variable_against_solution [%ty : int] name @ + check_count_fun code_ast name "+" 0) + +let exercise = + [ testadd "phrase0"; + testadd "phrase1"; + testadd "phrase2"; + testadd "phrase3"; + testmult "phrase4"] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/1.3_bool/descr.md b/exercises/hferee/1.3_bool/descr.md new file mode 100644 index 0000000..f6afa26 --- /dev/null +++ b/exercises/hferee/1.3_bool/descr.md @@ -0,0 +1,71 @@ +#### Reminder: *bool* expressions + +Boolean expressions can be: + +- Boolean constants `true` and `false`. + +- Comparison expressions: `e1 cmp e2` where *cmp* is one of the operators + `=` , `<>`, `<`, `<=` , `>` ou `>=`, and *e1* and *e2* are of the same + type. +- Expressions constructed with logical operators: + - `e1 && e2` (and) + - `e1 || e2` (or) + - `not e` (negation of) + where *e1*, *e2* and *e* of type bool. + +Beware of the `==` and `!=` comparisons: they should **never** be used in +OCaml unless you know exactly what you are doing (they perform *physical* +or *pointer* comparisons. For example, `(1,2)==(1,2)` will yield `false`). + +Functions cannot be compared directly, but integers, characters, lists, +tuples, etc., can be compared. For numeric types, the usual order on +numbers is used; for characters, alphabetical order is used; for strings, +lexicographic order is used; for tuples, lexicographic order is used based +on their components, and so on. + +** Question 1.** In the following code, replace `false` with a boolean +expression that evaluates to `true` if and only if the number `x` is within +(inclusive) the range from `0` to `10`. + + +```ocaml +let intervalle10 x = false +``` + +**Question 2.** In the following declarations, replace the boolean +expression with its value (`true` or `false`) + +```ocaml +let valeur1 = true && false + +let valeur2 = true || false + +let valeur3 = not (true || true) && true +``` + +**Question 3.** In the following declarations, replace the expression on the +right-hand side with a simpler equivalent expression. + +```ocaml +let simplifier1 x y = (x || y) || x + +let simplifier2 x y = (x > 5 && x >= 7) + +let simplifier3 x y z = x = y && y = z && x = z + +let simplifier4 x y = x > 7 || (x <= 7 && y > 2) + +let simplifier5 x = (((x = true) = false) = false) = true +``` + +**Question 4.** A leap year is a year whose number is divisible by 4, except if it is +also divisible by 100... unless it is also divisible by 400. In the following declaration, +replace `false` with a boolean expression that evaluates to `true` only when `x` is an +integer corresponding to a leap year. + +```ocaml +let bissextile x = false +``` + +It is worth noting that the `mod` operator calculates the remainder of the division operation. +Specifically, `x mod y` is equal to `0` when `x` is divisible by `y`. diff --git a/exercises/hferee/1.3_bool/meta.json b/exercises/hferee/1.3_bool/meta.json new file mode 100644 index 0000000..54d00dd --- /dev/null +++ b/exercises/hferee/1.3_bool/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Booleans", + "backward_exercises": ["mooc/week1/seq4/ex2"] +} diff --git a/exercises/hferee/1.3_bool/prelude.ml b/exercises/hferee/1.3_bool/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/1.3_bool/prepare.ml b/exercises/hferee/1.3_bool/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/1.3_bool/solution.ml b/exercises/hferee/1.3_bool/solution.ml new file mode 100644 index 0000000..e239490 --- /dev/null +++ b/exercises/hferee/1.3_bool/solution.ml @@ -0,0 +1,24 @@ +(* Question 1. *) +let intervalle10 x = x >= 0 && x <= 10 + +(* Question 2. *) +let valeur1 = false + +let valeur2 = true + +let valeur3 = false + +(* Question 3.*) +let simplifier1 x y = x || y + +let simplifier2 x y = x >= 7 + +let simplifier3 x y z = x = y && y = z + +let simplifier4 x y = x > 7 || y > 2 + +let simplifier5 x = x + +(*Question 4. *) + +let bissextile x = (x mod 4 = 0) && (x mod 100 <> 0 || x mod 400 = 0) diff --git a/exercises/hferee/1.3_bool/template.ml b/exercises/hferee/1.3_bool/template.ml new file mode 100644 index 0000000..56e1ec5 --- /dev/null +++ b/exercises/hferee/1.3_bool/template.ml @@ -0,0 +1,24 @@ +(* Question 1. *) +let intervalle10 x = false + +(* Question 2. *) +let valeur1 = true && false + +let valeur2 = true || false + +let valeur3 = not (true || true) && true + +(* Question 3.*) +let simplifier1 x y = (x || y) || x + +let simplifier2 x y = (x > 5 && x >= 7) + +let simplifier3 x y z = x = y && y = z && x = z + +let simplifier4 x y = x > 7 || (x <= 7 && y > 2) + +let simplifier5 x = (((x = true) = false) = false) = true + +(*Question 4. *) + +let bissextile x = false diff --git a/exercises/hferee/1.3_bool/test.ml b/exercises/hferee/1.3_bool/test.ml new file mode 100644 index 0000000..43f5e9c --- /dev/null +++ b/exercises/hferee/1.3_bool/test.ml @@ -0,0 +1,63 @@ +open Test_lib +open Report + +let testint name = + Section ([ Code name ], test_variable_against_solution [%ty : int] name) + +(* checks that the number of occurrences of function f in code_ast is n *) +(* status is usually Warning or Failure + no report is done if the constraints are satisfied *) +let check_one_count name f n msg status = + let count = ref 0 in + let reports = find_binding code_ast name (ast_check_expr ~on_variable_occurence: + (fun v -> if v = f then incr count; [])) + in + if !count <> n then [Message ([Text msg], status)] + else [] + +(* hide successful tests and only keep the first failure*) +let hide l msg = match List.filter (fun m -> match m with | Message (_, Failure _) -> true | _ -> false) l with + | [] -> [Message ([Text msg], Success 1)] (* no failure *) + | h :: t -> [h] (* only keep the first one *) + +let check_count name l test status= + Section ([Code name], + hide test "Valeur correcte sur un ensemble de tests" @ + hide (List.concat(List.map (fun (f, n) -> check_one_count name f n "Simplifier davantage." status) l)) "Suffisamment simplifié.") + +let bools = [true; false] + +let prod_bools l = List.map (fun x -> (true, x)) l @ + List.map (fun x -> (false, x)) l +let bools2 = prod_bools bools +let bools3 = prod_bools bools2 + + +let exercise = + [ + Section ([Code "intervalle10"], hide (test_function_1_against_solution [%ty : int -> bool] "intervalle10" ~gen:0 [-1; 0; 5; 9; 10; 11; 42]) "valeur correcte"); + check_count "valeur1" [("&&", 0); ("||", 0); ("not", 0)] (test_variable_against_solution [%ty : bool] "valeur1") Failure; + check_count "valeur2" [("&&", 0); ("||", 0); ("not", 0)] (test_variable_against_solution [%ty : bool] "valeur2") Failure; + check_count "valeur3" [("&&", 0); ("||", 0); ("not", 0)] (test_variable_against_solution [%ty : bool] "valeur3") Failure; + + check_count "simplifier1" [("&&", 0); ("||", 1); ("not", 0)] + (test_function_2_against_solution [%ty : bool -> bool -> bool] ~gen:0 "simplifier1" bools2) Failure; + check_count "simplifier2" [("&&", 0); ("||", 0); ("not", 0)] + (test_function_2_against_solution [%ty : int -> int -> bool] ~gen:10 "simplifier2" [5, 6]) Failure; + + check_count "simplifier3" [("&&", 1); ("=", 2); ("||", 0)] + (test_function_3_against_solution [%ty : int -> int -> int -> bool] ~gen:20 "simplifier3" []) Failure; + check_count "simplifier4" [("&&", 0); ("||", 1); ("not", 0)] + (test_function_2_against_solution [%ty : int -> int -> bool] ~gen:10 "simplifier4" [(7, 2); (7, 3); (8, 2); (8, 3)]) Failure; + check_count "simplifier5" [("&&", 0); ("||", 0); ("not", 0); ("true", 0); ("false", 0); ("=", 0)] + (test_function_1_against_solution [%ty : bool -> bool] ~gen:0 "simplifier5" bools) Failure; + check_count "bissextile" [("mod", 0); ("||", 1); ("&&", 1)] + (test_function_1_against_solution [%ty : int -> bool] ~gen:100 "bissextile" [0; 4; 100; 200; 400; 700; 800; 804]) Warning + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/1.4_conditionals/descr.md b/exercises/hferee/1.4_conditionals/descr.md new file mode 100644 index 0000000..82a08a2 --- /dev/null +++ b/exercises/hferee/1.4_conditionals/descr.md @@ -0,0 +1,73 @@ +## Reminder: Conditional expressions + +If `c` is an expression of type `bool` and `e1` and `e2` are expressions of the +same type, then `if c then e1 else e2` is an expression of the same type as `e1` +and `e2`. + +**Question 1.** + +For each of the following expressions, give its value, or `Erreur` if it doesn't +make sense. + +```ocaml +let phrase1 = if false then 3 else 4 + +let phrase2 = if true then 5 else 6. + +let phrase3 = if 3 + 4 then true else false +``` + +**Question 2.** + +A conditional expression allows us to construct an expression using other expressions. +These other expressions can themselves be constructed using a conditional expression, +and so on (we say they are _nested_). + +Replace the following conditional expressions with equivalent expressions containing +strictly fewer `if` statements. + +```ocaml +let simplifie1 x = if x > 3 then false else true + +let simplifie2 x y= + if x then + if y then false + else true + else + if y then true + else false + +let edt day time = + if day = "monday" + then if 13 * 60 + 30 <= time && time < 15 * 60 + 30 then "practical" + else "Nothing interesting" + else if day = "thursday" + then if 8 * 60 + 30 <= time && time < 10 * 60 + 30 then "lecture-tutorial" + else "Nothing interesting" + else "Nothing interesting" +``` + +**Question 3 ($$).** + +In the following declaration, replace `"Approximately ..."` with an expression that approximately +expresses in days, hours, minutes, or seconds duration `t` expressed in seconds. For example, the +expression should evaluate to `"Approximately 3 hours"` if `t` is `9000`, or `"Approximately 2 days"` +if `t` is `180000`. For negative numbers, the result is not important. + +_Note_: You can use the `string_of_int` function to convert an integer to a string. + +```ocaml +let approximately t = "Approximately …" +``` + + +**Bonus ($$$)** + +Same question, but correctly adjust the words "seconds", "minutes", "hours", and "days" according to the value. + +_Note_: For this question, it is much more convenient to use tuples. + +```ocaml +let approximately_bonus t = "Approximately …" +``` + diff --git a/exercises/hferee/1.4_conditionals/meta.json b/exercises/hferee/1.4_conditionals/meta.json new file mode 100644 index 0000000..b7726e1 --- /dev/null +++ b/exercises/hferee/1.4_conditionals/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Conditional expressions", + "backward_exercises": ["hferee/1.3_bool"] +} diff --git a/exercises/hferee/1.4_conditionals/prelude.ml b/exercises/hferee/1.4_conditionals/prelude.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/1.4_conditionals/prelude.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/1.4_conditionals/prepare.ml b/exercises/hferee/1.4_conditionals/prepare.ml new file mode 100644 index 0000000..f718b2a --- /dev/null +++ b/exercises/hferee/1.4_conditionals/prepare.ml @@ -0,0 +1 @@ +exception Erreur diff --git a/exercises/hferee/1.4_conditionals/solution.ml b/exercises/hferee/1.4_conditionals/solution.ml new file mode 100644 index 0000000..202bd02 --- /dev/null +++ b/exercises/hferee/1.4_conditionals/solution.ml @@ -0,0 +1,34 @@ +let phrase1 = 4 + +let phrase2 = Erreur + +let phrase3 = Erreur + + +let simplifie1 x = x <= 3 + +let simplifie2 x y = if x then not y else y + +let edt day time = + if day = "monday" && 13 * 60 + 30 <= time && time < 15 * 60 + 30 then "practical" + else if day = "thursday" && 8 * 60 + 30 <= time && time < 10 * 60 + 30 then "lecture-tutorial" + else "Nothing interesting" + +let approximately t = "Approximately " ^ + if t >= 24*3600 then string_of_int (t / (24 * 3600)) ^ " days" + else if t >= 3600 then string_of_int (t / 3600) ^ " hours" + else if t >= 60 then string_of_int (t / 60) ^ " minutes" + else if t >= 0 then string_of_int t ^ " seconds" + else "Negative number" + + +let approximately_bonus t = + let (n, s) = + if t >= 24*3600 then (t / (24 * 3600), "day") + else if t >= 3600 then (t / 3600, "hour") + else if t >= 60 then (t / 60, "minute") + else (t, "second") + in + if n < 0 then "Negative number" else + let pluriel = if n <> 1 then "s" else "" in + "Approximately " ^ string_of_int n ^ " " ^ s ^ pluriel diff --git a/exercises/hferee/1.4_conditionals/template.ml b/exercises/hferee/1.4_conditionals/template.ml new file mode 100644 index 0000000..020b66c --- /dev/null +++ b/exercises/hferee/1.4_conditionals/template.ml @@ -0,0 +1,22 @@ +(* Question 1 *) +let phrase1 = "Remplacez-moi" + +let phrase2 = "Remplacez-moi" + +let phrase3 = "Remplacez-moi" + + +(* Question 2 *) +let simplifie1 x = "Remplacez-moi" + +let simplifie2 x y = "Remplacez-moi" + +let edt day time = "Remplacez-moi" + + +(* Question 3 *) + +let approximately t = "Approximately …" + +let approximately_bonus t = "Approximately …" + diff --git a/exercises/hferee/1.4_conditionals/test.ml b/exercises/hferee/1.4_conditionals/test.ml new file mode 100644 index 0000000..1a907a7 --- /dev/null +++ b/exercises/hferee/1.4_conditionals/test.ml @@ -0,0 +1,82 @@ + +open Test_lib +open Report +open Parsetree +open Longident + +let testint name = + Section ([ Code name ], test_variable_against_solution [%ty : int] name) + +let testexn name = + Section ([ Code name ], test_variable_against_solution [%ty : exn] name) + +let sample_pos () = abs(sample_int()) + +let test_construct c (e : expression) = match e.pexp_desc with +| Pexp_construct (id, _) when id.txt = Lident c -> true +| _ -> false + +(* checks that the number of occurrences a construct in code_ast is n *) +(* status is usually Warning or Failure + no report is done if the constraints are satisfied *) +let check_one_count name f n msg status = + let count = ref 0 in + let reports = find_binding code_ast name (ast_check_expr ~on_expression: + (fun v -> if f v then incr count; [])) + in + if !count <> n then [Message ([Text msg], status)] + else [] + +(* hide successful tests and only keep the first failure*) +let rec hide l msg = match l with + | [] -> [Message ([Text msg], Success 1)] (* no failure *) + | h :: Message (txt, Failure) :: t -> [h; Message (txt, Failure)] (* only keep the first failure with its previous message *) + | h :: t -> hide t msg + +let check_count name l test status= + Section ([Code name], test @ + if l = [] then [] + else hide (List.concat(List.map (fun (f, n) -> check_one_count name f n "Simplifier davantage." status) l)) "Suffisamment simplifié.") + +let bools = [true; false] + +let prod_bools l = List.map2 (fun x y -> (x, y)) bools l +let bools2 = prod_bools bools +let bools3 = prod_bools bools2 + +let test_string s (e : expression) = match e.pexp_desc with +| Pexp_constant (Pconst_string (s', _, _)) when s' = s -> true +| _ -> false + +let exercise = + [testint "phrase1"; + testexn "phrase2"; + testexn "phrase3"; + + check_count "simplifie1" [(test_construct "true", 0); (test_construct "false", 0)] + (test_function_1_against_solution [%ty : int -> bool] ~gen:0 "simplifie1" [-1; 0; 1; 2; 100]) Failure; + check_count "simplifie2" [(test_construct "true", 0); (test_construct "false", 0)] + (test_function_2_against_solution [%ty : bool -> bool -> bool] ~gen:0 "simplifie2" bools2) Failure; + check_count "edt" [(test_string "Nothing interesting" , 1)] + (test_function_2_against_solution [%ty : string -> int -> string] ~gen:5 "edt" + [("monday", 13*60+29); ("monday", 13*60+30); ("monday", 15*60+30); ("monday", 14); + ("thursday", 8*60+29); ("thursday", 8*60+30); ("thursday", 8*60+31); ("thursday", 10 * 60 + 30)]) Failure; + + check_count "approximately" [] + (test_function_1_against_solution [%ty : int -> string] ~gen:10 + ~sampler: sample_pos "approximately" + [24*3600; 0; 24*3600 + 1; 24*3600; 3600 +1; 3600 -1; 3600; 59; 60; 61]) Failure; + + check_count "approximately_bonus" [] + (test_function_1_against_solution [%ty : int -> string] ~gen:10 + ~sampler: sample_pos "approximately_bonus" + [24*3600; 0; 24*3600 + 1; 24*3600; 3600 +1; 3600 -1; 3600; 59; 60; 61]) Failure; + + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/10_assoc/descr.md b/exercises/hferee/10_assoc/descr.md new file mode 100644 index 0000000..1a9f257 --- /dev/null +++ b/exercises/hferee/10_assoc/descr.md @@ -0,0 +1,55 @@ +We recall the parameterized type `option`: +```ocaml +type 'a t = 'a option = +| None +| Some of 'a +``` + +If `t` is a type, values of type `t option` represent at most one value of type `t`. + +A typical example of using the `option` type is the definition of partial functions. For example, a function of type `int -> string option` sometimes returns a string, and sometimes nothing. + +------ + +**Question 1**: +Define a function `option_map: ('a -> 'b) -> 'a option -> 'b option` that applies a function to an argument that may or may not be a value. We can try to use this function in the subsequent questions. + +**Question 2**: +Define the functions `hd_opt: 'a list -> 'a option` and `tl_opt: 'a list -> ('a list) option` that respectively calculate the head and tail of a list, if possible. + +**Question 3**: +Using the `max` function, define the function `list_max`, which returns an option value representing the largest element of the list, if it exists. + +For example, `list_max [1; 3; 2] = Some 3` and `list_max [] = None`. + +Observe the type of `list_max`. + +Generalize it to a function `list_greatest` such that `list_greatest le l` returns the greatest element of `l` according to the order `le: 'a -> 'a -> bool` (`le a b = true` if `a` is smaller than `b`). + +**Question 4**: +Define the function `find_opt: ('a -> bool) -> 'a list -> 'a option` such that `find_opt f l` returns the first element of `l` that satisfies `f`, if it exists. + +**Question 5**: +Define `filter_map: ('a -> 'b option) -> 'a list -> 'b list` such that `filter_map f l` returns the list (in order) of values `v` such that `f x = Some v` and `x` is an element of `l`. + +----------- + +An association list is a list of the form `('a * 'b) list`. They are particularly useful for storing data in the form of a pair `(key, value)`. + +**Question 6**: +Write a function `assoc_opt` that takes a key, an association list, and returns, if it exists, a value associated with that key in the list. + +**Question 7**: +We represent the student notes in a list associating a student's name (of type `string`) with their grade (of type `float option`); the grade `None` represents an absence: +```ocaml +type grades = (string * float option) list +``` + +The `assoc_opt` function allows us to retrieve the grade associated with a student. + +Using only standard list functions, and without defining any recursive functions, define the following functions that take a list of type `grades` as an argument: + +- The `average` function calculates the average grade considering absences as `0`. +- The `average_present` function calculates the average grade ignoring absences. +- The `max_grade` function returns the highest grade. +- The `best` function returns the name of a student with the highest grade. diff --git a/exercises/hferee/10_assoc/meta.json b/exercises/hferee/10_assoc/meta.json new file mode 100644 index 0000000..8935830 --- /dev/null +++ b/exercises/hferee/10_assoc/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Option type and association lists", + "backward_exercises": ["hferee/10_parameterized_lists"] +} diff --git a/exercises/hferee/10_assoc/prelude.ml b/exercises/hferee/10_assoc/prelude.ml new file mode 100644 index 0000000..33b0bd0 --- /dev/null +++ b/exercises/hferee/10_assoc/prelude.ml @@ -0,0 +1 @@ +open List diff --git a/exercises/hferee/10_assoc/prepare.ml b/exercises/hferee/10_assoc/prepare.ml new file mode 100644 index 0000000..cb69256 --- /dev/null +++ b/exercises/hferee/10_assoc/prepare.ml @@ -0,0 +1 @@ +type grades = (string * float option) list diff --git a/exercises/hferee/10_assoc/solution.ml b/exercises/hferee/10_assoc/solution.ml new file mode 100644 index 0000000..4a78bb4 --- /dev/null +++ b/exercises/hferee/10_assoc/solution.ml @@ -0,0 +1,67 @@ +open List + +let option_map f = function + | None -> None + | Some x -> Some (f x) + +let hd_opt = function + | [] -> None + | h :: _ -> Some h + +let tl_opt = function + | [] -> None + | _ :: t -> Some t + +let rec list_max = function + | [] -> None + | h :: t -> match list_max t with + | None -> Some h + | Some m -> Some (max m h) + + +let rec list_greatest le = function + | [] -> None + | h :: t -> Some (match list_greatest le t with + | None -> h + | Some m -> if le h m then m else h) + + +let rec find_opt f = function + | [] -> None + | h :: t -> if f h then Some h else find_opt f t + +let rec filter_map f = function + | [] -> [] + | h :: t -> match f h with + | None -> filter_map f t + | Some h' -> h' :: filter_map f t + +let assoc_opt x l = + option_map snd (find_opt (fun (k, v) -> k = x) l) + + +let value default = function + | None -> default + | Some x -> x + +let average l = + let lg = map snd l in + let lc = map (value 0.) lg in + let sum = fold_left (+.) 0. lc in + let ll = float_of_int (length lc) in + if ll = 0.0 then None else Some (sum /. ll) + +let average_present l = + let lg = map snd l in + let lc = filter_map (fun x -> x) lg in + let sum = fold_left (+.) 0. lc in + let ll = float_of_int (length lc) in + if ll = 0.0 then None else Some (sum /. ll) + +let max_grade l = + l |> map snd |> filter_map (fun x -> x) |> list_max + +let best l = + l |> filter_map (fun (e, g) -> option_map (fun n -> e, n) g) + |> list_greatest (fun (_, g1) (_, g2) -> g1 < g2) + |> option_map fst diff --git a/exercises/hferee/10_assoc/template.ml b/exercises/hferee/10_assoc/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/10_assoc/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/10_assoc/test.ml b/exercises/hferee/10_assoc/test.ml new file mode 100644 index 0000000..2f7e301 --- /dev/null +++ b/exercises/hferee/10_assoc/test.ml @@ -0,0 +1,91 @@ + +open Test_lib +open Report + +let notes = [[]; ["x", None]; [("a", Some 1.5); ("b", Some 15.5); ("c", None); ("d", Some 11.5)]] + +let exercise = + [ + Section( + [Text "Question 1"], + test_function_2_against_solution + [%ty :(int -> float) -> int option -> float option] ~gen:0 "option_map" + [float_of_int, None] @ + test_function_2_against_solution + [%ty :(float -> int) -> float option -> int option] ~gen:0 "option_map" + [int_of_float, Some 3.14]); + + Section( + [Text "Question 2"], + test_function_1_against_solution + [%ty :(int list) -> int option] ~gen:0 "hd_opt" + [[]; [4; 2]] @ + test_function_1_against_solution + [%ty :(unit list) -> unit option] ~gen:0 "hd_opt" + [[()]] @ + test_function_1_against_solution + [%ty :(int list) -> int list option] ~gen:0 "tl_opt" + [[]; [4;2]] @ + test_function_1_against_solution + [%ty :(unit list) -> unit list option] ~gen:0 "tl_opt" + [[()]]); + + Section( + [Text "Question 3"], + test_function_1_against_solution + [%ty :(int list) -> int option] ~gen:0 "list_max" + [[]; [4;2]] @ + test_function_1_against_solution + [%ty :(float list) -> float option] ~gen:0 "list_max" + [[3.4; 2.4]] @ + test_function_2_against_solution + [%ty : (int -> int -> bool) -> (int list) -> int option] ~gen:0 "list_greatest" + [(<), []; (>), [4;2]] @ + test_function_2_against_solution + [%ty :(float -> float -> bool) -> (float list) -> float option] ~gen:0 "list_greatest" + [(fun x y -> 1./.x < 1./.y), [3.4; 2.4; 0.001]]); + + Section( + [Text "Question 4"], + test_function_2_against_solution + [%ty :(float -> bool) -> (float list) -> float option] ~gen:0 "find_opt" + [(<) 2., [3.4; 2.4; 0.001]]); + + Section( + [Text "Question 5"], + test_function_2_against_solution + [%ty :(float -> int option) -> (float list) -> int list] + ~gen:0 "filter_map" + [(fun x -> let i = int_of_float x in if float_of_int i = x then Some i + else None), [3.4; 2.; 0.001]]); + + Section( + [Text "Question 6"], + test_function_2_against_solution + [%ty : float -> ((float * int) list) -> int option] + ~gen:0 "assoc_opt" + [2.4, [(3.4, 1); (2.4, 2); (0.001, 3)]; 3.14, []]); + + + Section( + [Text "Question 7"], + test_function_1_against_solution + [%ty :(string * float option) list -> float option] ~gen:0 "average" + notes @ + test_function_1_against_solution + [%ty :(string * float option) list -> float option] ~gen:0 "average_present" + notes @ + test_function_1_against_solution + [%ty :(string * float option) list -> float option] ~gen:0 "max_grade" + notes @ + test_function_1_against_solution + [%ty :(string * float option) list -> string option] ~gen:0 "best" + notes) + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/10_parameterized_lists/descr.md b/exercises/hferee/10_parameterized_lists/descr.md new file mode 100644 index 0000000..e2a365e --- /dev/null +++ b/exercises/hferee/10_parameterized_lists/descr.md @@ -0,0 +1,32 @@ +In the standard library of OCaml, lists are defined as follows: + +```ocaml +type 'a t = 'a list = + | [] + | (::) of 'a * 'a list +``` + +This means that for every type `t`, we can define and manipulate lists whose elements have the type `t`. + +**Question 1** + +Define lists `l3_int`, `l3_float`, `l3_bool`, `l3_int_int`, each containing three elements and respectively containing integers, floats, booleans, and pairs of integers. + +**Question 2** _(no automatic validation)_ + +Observe the type of the list `empty`: +```ocaml +let empty = [] +``` + +What types of elements can be added to this list? Test it in the toplevel. + +Define a variable that represents an empty list to which only booleans can be added. Check its type and test in the toplevel that nothing else can be added to it. + +------ + +This parameterized definition of the list type allows us to define polymorphic functions that work on all lists, regardless of the type of their elements. + +**Question 3** + +Define the polymorphic functions `length`, `map`, `fold_left`, and `fold_right`. Observe their types. diff --git a/exercises/hferee/10_parameterized_lists/meta.json b/exercises/hferee/10_parameterized_lists/meta.json new file mode 100644 index 0000000..f2ecc46 --- /dev/null +++ b/exercises/hferee/10_parameterized_lists/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Parameterized lists", + "backward_exercises": ["mooc/week2/seq3/ex2"] +} diff --git a/exercises/hferee/10_parameterized_lists/prelude.ml b/exercises/hferee/10_parameterized_lists/prelude.ml new file mode 100644 index 0000000..5dc9eed --- /dev/null +++ b/exercises/hferee/10_parameterized_lists/prelude.ml @@ -0,0 +1 @@ +let empty = [] diff --git a/exercises/hferee/10_parameterized_lists/prepare.ml b/exercises/hferee/10_parameterized_lists/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/10_parameterized_lists/solution.ml b/exercises/hferee/10_parameterized_lists/solution.ml new file mode 100644 index 0000000..dfe6215 --- /dev/null +++ b/exercises/hferee/10_parameterized_lists/solution.ml @@ -0,0 +1,12 @@ +let l3_int = 1 :: 2 :: 3 :: [] +let l3_float = 1. :: 2. :: 3. :: [] +let l3_bool = true :: false :: false :: [] +let l3_int_int = (1,0) :: (2,1) :: (3,3) :: [] + +let length = List.length + +let map = List.map + +let fold_left = List.fold_left + +let fold_right = List.fold_right diff --git a/exercises/hferee/10_parameterized_lists/template.ml b/exercises/hferee/10_parameterized_lists/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/10_parameterized_lists/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/10_parameterized_lists/test.ml b/exercises/hferee/10_parameterized_lists/test.ml new file mode 100644 index 0000000..9a28d03 --- /dev/null +++ b/exercises/hferee/10_parameterized_lists/test.ml @@ -0,0 +1,59 @@ + +open Test_lib +open Report + +let l3 ty s = + test_variable_property ty s + (fun l -> + [Message ([Code s], + if List.length l = 3 + then Success 1 + else Failure)]) + + +let exercise = + [ + Section([Text "Question 1"], + l3 [%ty: int list] "l3_int" @ + l3 [%ty: float list] "l3_float" @ + l3 [%ty: bool list] "l3_bool" @ + l3 [%ty: (int*int) list] "l3_int_int"); + + Section( + [Code "length"], + test_function_1_against_solution [%ty: int list -> int] "length" ~gen:0 + [[]; [1; 3; 5]] @ + test_function_1_against_solution [%ty: unit list -> int] "length" ~gen:0 + [[()]]); + Section( + [Code "map"], + test_function_2_against_solution [%ty: (int -> string) -> int list -> + string list] "map" ~gen:0 + [string_of_int, [1; 3; 5]] @ + test_function_2_against_solution + [%ty: (string -> int) -> string list -> int list] + "map" ~gen:0 [int_of_string, []]); + Section( + [Code "fold_left"], + test_function_3_against_solution [%ty: (string -> int -> string) -> + string -> int list -> string] "fold_left" ~gen:0 + [(fun s i -> s ^ string_of_int i), "", [1; 3; 5]] @ + test_function_3_against_solution + [%ty: (float -> unit -> float) -> float -> unit list -> float] + "fold_left" ~gen:0 [(fun x y -> x), 3.14, []]); + Section( + [Code "fold_right"], + test_function_3_against_solution [%ty: (int -> string -> string) -> + int list -> string -> string] "fold_right" ~gen:0 + [(fun i s -> s ^ string_of_int i), [1; 3; 5], ""] @ + test_function_3_against_solution [%ty: (unit -> float -> float) + -> unit list -> float -> float] "fold_right" ~gen:0 + [(fun x y -> y), [], 3.14]); +] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/10_run_length/descr.md b/exercises/hferee/10_run_length/descr.md new file mode 100644 index 0000000..54b86cb --- /dev/null +++ b/exercises/hferee/10_run_length/descr.md @@ -0,0 +1,39 @@ +# Encode by Range + +Range encoding is a lossless data compression algorithm. It involves indicating the number of times an element should be represented. + +For example, the sequence of letters `aabbbabb` would be represented as `a2b3a1b2`. + +**Question 1**: +Write a function `encode` that encodes a list (of arbitrary elements) into a list of pairs representing its range encoding. + +For example, +```ocaml +encode [1; 1; 1; 2; 2; 1; 2; 2] = [(1, 3); (2, 2); (1, 1); (2, 2)] +``` + +**Question 2**: +Define the function `decode` which satisfies the following properties for any lists `l` and `lp`: +```ocaml +encode (decode lp) = lp +decode(encode l) = l +``` + +**Question 3**: +Observe the following sequence: +``` +1 +1 1 +2 1 +1 2 1 1 +1 1 1 2 2 1 +3 1 2 2 1 1 +1 3 1 1 2 2 2 1 +1 1 1 3 2 1 3 2 1 1 +… +``` + +Define the function `mystery` such that for any positive integer `n`, `mystery(n)` calculates the n-th line of this logical sequence, with `mystery 0 = [1]`. + +**Bonus**: +Define the function `list_mystery` which, given a positive integer `n`, represents the first `n + 1` lines of the logical sequence. diff --git a/exercises/hferee/10_run_length/meta.json b/exercises/hferee/10_run_length/meta.json new file mode 100644 index 0000000..a4d377b --- /dev/null +++ b/exercises/hferee/10_run_length/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 3, + "title": "Encode by range", + "backward_exercises": ["mooc/week3/seq2/ex1"] +} diff --git a/exercises/hferee/10_run_length/prelude.ml b/exercises/hferee/10_run_length/prelude.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/10_run_length/prelude.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/10_run_length/prepare.ml b/exercises/hferee/10_run_length/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/10_run_length/solution.ml b/exercises/hferee/10_run_length/solution.ml new file mode 100644 index 0000000..fda7b1c --- /dev/null +++ b/exercises/hferee/10_run_length/solution.ml @@ -0,0 +1,35 @@ + + + +let rec encode = function + | [] -> [] + | h:: t -> match encode t with + | [] -> (h, 1) :: [] + | (h', n) :: t' -> + if h = h' then (h, n + 1) :: t' + else (h, 1) :: (h', n) :: t' + + +let rec decode = function + | [] -> [] + | (h, 0) :: t -> decode t + | (h, n) :: t -> h :: decode ((h, n - 1) :: t) + +let rec split_pairs = function + | [] -> [] + | (h, h') :: t -> h' :: h :: split_pairs t + +let rec mystery n = + if n <= 0 then [1] + else split_pairs (encode (mystery (n - 1))) + + +(* bonus *) + +let list_mystery n= + let rec aux k last acc = + if k >= n then last :: acc else + aux (k + 1) (split_pairs (encode last)) (last :: acc) + in List.rev (aux 0 [1] []) + + diff --git a/exercises/hferee/10_run_length/template.ml b/exercises/hferee/10_run_length/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/10_run_length/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/10_run_length/test.ml b/exercises/hferee/10_run_length/test.ml new file mode 100644 index 0000000..cfcd269 --- /dev/null +++ b/exercises/hferee/10_run_length/test.ml @@ -0,0 +1,32 @@ + +open Test_lib +open Report + +let exercise = + [ + Section( + [Code "encode"], + test_function_1_against_solution [%ty: int list -> (int * int) list] + ~gen:0 "encode" [[1; 1; 1; 2; 0; 1]] @ + test_function_1_against_solution [%ty: float list -> (float * int) list] + ~gen:0 "encode" [[]]); + + Section( + [Code "decode"], + test_function_1_against_solution [%ty: (int * int) list -> int list] + ~gen:0 "decode" [[1, 3; 2, 1; 3, 2]] @ + test_function_1_against_solution [%ty: (float * int) list -> float list] + ~gen:0 "decode" [[]]); + + Section( + [Code "mystery"], + test_function_1_against_solution [%ty: int -> int list] + ~gen:3 ~sampler:(fun x -> abs(sample_int())) "mystery" [0; 1; 10]) + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/11_lists_lists/descr.md b/exercises/hferee/11_lists_lists/descr.md new file mode 100644 index 0000000..0a07c35 --- /dev/null +++ b/exercises/hferee/11_lists_lists/descr.md @@ -0,0 +1,21 @@ +**Question 1**: + +Write a function `list_list_get: 'a list list -> int -> int -> 'a option` such that `list_list_get l n m` represents the m-th element of the n-th list in l. If this element does not exist, return `None`. + +**Question 2**: +Write a function `transpose: 'a list list -> 'a list list` that transposes a list of lists. In other words, the i-th row of `transpose l` is composed of the i-th elements (in order) of the lists in l. + +**Note**: +In which case do we have `transpose (transpose l) = l`? + +**Question 3**: + +Here, we implement a particularly efficient sorting algorithm: merge sort. + +1. Write a function `merge: int list -> int list -> int list` that merges two given sorted lists into a single sorted list. + For example, `merge [1;2;3] [0;1;4;5;6]` should return `[0;1;1;2;3;4;5;6]`. +2. Write a function `wrap: int list -> int list list` that takes a list as an argument and replaces each element in the list with a list containing that element. + For example, `wrap [1;2;3]` should return `[[1];[2];[3]]`. +3. Write a function `flatten_merge: int list list -> int list list` that, given a list of lists as an argument, merges the elements pairwise into sorted lists. + For example, `flatten_merge [[2];[0];[5];[4];[3]]` should return `[[0;2];[4;5];[3]]`. +4. Using the previous functions, write a function `merge_sort: int list -> int list` that sorts the given list. diff --git a/exercises/hferee/11_lists_lists/meta.json b/exercises/hferee/11_lists_lists/meta.json new file mode 100644 index 0000000..c9dd666 --- /dev/null +++ b/exercises/hferee/11_lists_lists/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Lists of lists", + "backward_exercises": ["hferee/10_run_length"] +} diff --git a/exercises/hferee/11_lists_lists/prelude.ml b/exercises/hferee/11_lists_lists/prelude.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/11_lists_lists/prelude.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/11_lists_lists/prepare.ml b/exercises/hferee/11_lists_lists/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/11_lists_lists/solution.ml b/exercises/hferee/11_lists_lists/solution.ml new file mode 100644 index 0000000..b886adc --- /dev/null +++ b/exercises/hferee/11_lists_lists/solution.ml @@ -0,0 +1,55 @@ +let rec list_get l n = match l, n with +| [], _ -> None +| h :: t, 0 -> Some h +| h :: t, _ -> list_get t (n - 1) + + +let list_list_get l n m = + match list_get l n with + | None -> None + | Some l' -> list_get l' m + +let hd_opt = function + | [] -> None + | h :: _ -> Some h + +let tl_opt = function + | [] -> None + | _ :: t -> Some t + +let rec filter_map f = function + | [] -> [] + | h :: t -> match f h with + | None -> filter_map f t + | Some h' -> h' :: filter_map f t + +let rec transpose l = match l with +| [] -> [] +| _ -> match filter_map hd_opt l with + | [] -> [] + | firsts -> firsts :: transpose (filter_map tl_opt l) + +let rec merge l1 l2 = + match l1, l2 with + | [], l | l, [] -> l + | x1::l1', x2::l2' -> + if x1 < x2 then + x1::(merge l1' l2) + else + x2::(merge l1 l2') + +let wrap = List.map (fun x -> [x]) + +let rec flatten_merge = function + | x1::x2::l' -> + merge x1 x2 :: flatten_merge l' + | l -> l + +let merge_sort l = + let rec aux l = + match l with + | [] -> [] + | xs ::[] -> xs + | _ -> aux @@ flatten_merge l + in + aux (wrap l) diff --git a/exercises/hferee/11_lists_lists/template.ml b/exercises/hferee/11_lists_lists/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/11_lists_lists/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/11_lists_lists/test.ml b/exercises/hferee/11_lists_lists/test.ml new file mode 100644 index 0000000..d72728c --- /dev/null +++ b/exercises/hferee/11_lists_lists/test.ml @@ -0,0 +1,43 @@ + +open Test_lib +open Report + +let exercise = + [ + Section( + [Code "list_list_get"], + test_function_3_against_solution + [%ty : int list list -> int -> int -> int option] + ~gen: 0 "list_list_get" [[], 0, 0; [[1]], 0, 1; [[1]], 1, 0] @ + test_function_3_against_solution + [%ty : unit list list -> int -> int -> unit option] + ~gen: 0 "list_list_get" [[[]], 1, 0; [[()]], -1, 0] + ); + + Section( + [Code "transpose"], + test_function_1_against_solution [%ty : int list list -> int list list] + ~gen: 0 "transpose" [[]; [[]]; [[1; 2; 3]; [4]; [5; 6]; []; [7; 8; 9; 10]]] + ); + + Section( + [Code "merge"], + test_function_2_against_solution [%ty : int list -> int list -> int list] + ~gen: 0 "merge" [[], [1; 2; 3]; [1; 2; 3], []; [1], [0; 2; 3]; + [1],[2; 3]; [1; 2; 5; 8; 11], [0; 6; 7; 8; 9; 12]] @ + test_function_1_against_solution [%ty : int list -> int list list] + ~gen: 0 "wrap" [[]; [3]; [1; 3;5; 7; 9]] @ + test_function_1_against_solution [%ty : int list list -> int list list] + ~gen: 0 "flatten_merge" [[]; [[]]; [[3]]; [[3; 5]; [2; 4; 7]]; + [[1; 3; 5]; [2; 4; 8]; [2]; [1; 6]]] @ + test_function_1_against_solution [%ty : int list -> int list] + ~gen: 5 "merge_sort" [[]; [3]; [1; 7; -1; 4; -3; 12]] + ) + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/11_printable/descr.md b/exercises/hferee/11_printable/descr.md new file mode 100644 index 0000000..9d251c4 --- /dev/null +++ b/exercises/hferee/11_printable/descr.md @@ -0,0 +1,30 @@ +**Question 1**: + +It is often useful to be able to display an OCaml value on the screen as a string, for example when debugging a program. A _printer_ for type _t_ is a function of type `t -> string`. + +```ocaml +type 'a show = Show of ('a -> string) +``` + +The goal of this exercise is to write a small library for manipulating generic printers, defined as the type `'a show` above. + +1. Write a function `print: 'a show -> 'a -> unit` such that `print s v` prints the value `v` to the standard output, followed by a newline. + +2. Define the printers `show_int: int show`, `show_float: float show`, and `show_string: show string` for integers, floats, and strings. Strings should be displayed between double quotes, for example `print show_string "toto"` should print `"toto"` to the standard output, including the quotes. + +3. Write a printer `show_string_l: string show` that displays a string along with its size in curly braces. For example, `print show_string_l "toto"` should print `"toto"{4}` to the standard output. + +4. Write a function `show_pair: 'a show -> 'b show -> ('a * 'b) show` that constructs a printer for a pair from printers for each of its components. For example, `print (show_pair show_int show_float) (1, 42.5)` should print `(1, 42.5)` to the standard output. + +5. Write a function `show_list: 'a show -> 'a list show` that constructs a printer for a list from a printer for its elements. The contents of the list should be displayed between square brackets and separated by semicolons. For example, `print (show_list (show_pair show_float show_string_l)) [(1.5, "foo"); (12., "bar")]` should print `[(1.5, "foo"{3}); (12., "bar"{3})]` to the standard output. + +6. Use `show_list` to define a variant `show_list_lines: 'a show -> 'a list show'` that displays each element on a separate line with an indentation at the beginning of the line. In particular, `print (show_list_lines show_int) [1; 2; 3]` will display +``` +[ + 1 + 2 + 3 +] +``` + + Deduce a printer `show_list_list: 'a show -> 'a list list show` that, given a printer for `'a`, displays a list of lists line by line, where each line represents elements of type `'a`. diff --git a/exercises/hferee/11_printable/meta.json b/exercises/hferee/11_printable/meta.json new file mode 100644 index 0000000..3f0533f --- /dev/null +++ b/exercises/hferee/11_printable/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Display", + "backward_exercises": ["hferee/11_lists_lists"] +} diff --git a/exercises/hferee/11_printable/prelude.ml b/exercises/hferee/11_printable/prelude.ml new file mode 100644 index 0000000..bf43078 --- /dev/null +++ b/exercises/hferee/11_printable/prelude.ml @@ -0,0 +1 @@ +type 'a show = Show of ('a -> string);; diff --git a/exercises/hferee/11_printable/prepare.ml b/exercises/hferee/11_printable/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/11_printable/solution.ml b/exercises/hferee/11_printable/solution.ml new file mode 100644 index 0000000..07ab59b --- /dev/null +++ b/exercises/hferee/11_printable/solution.ml @@ -0,0 +1,27 @@ + +let print (Show s) v = print_string (s v ^ "\n");; + +let show_int = Show string_of_int;; +let show_float = Show string_of_float;; +let show_string = Show (fun s -> "\"" ^ s ^ "\"");; + +let show_string_l = Show (fun s -> "\"" ^ s ^ "\"(" ^ string_of_int (String.length s) ^ ")");; + +let show_pair (Show sx) (Show sy) = + Show (fun (x, y) -> "(" ^ sx x ^ ", " ^ sy y ^ ")");; + +let show_list (Show s) = + let rec aux xs = + match xs with + | [] -> "" + | [x] -> s x + | x :: xs -> s x ^ "; " ^ aux xs + in + Show (fun xs -> "[ " ^ aux xs ^ " ]") +;; + +let show_list_lines (Show s) = + show_list (Show (fun a -> " " ^ s a ^ "\n")) + +let show_list_list s = + show_list_lines (show_list s) diff --git a/exercises/hferee/11_printable/template.ml b/exercises/hferee/11_printable/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/11_printable/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/11_printable/test.ml b/exercises/hferee/11_printable/test.ml new file mode 100644 index 0000000..c986b3c --- /dev/null +++ b/exercises/hferee/11_printable/test.ml @@ -0,0 +1,30 @@ + +open Test_lib +open Report + +let testint name = + Section ([ Code name ], test_variable_against_solution [%ty : int] name) + + +let exercise = + [ + (* + Section( + [Text "Question 1"], + test_function_2_against_solution [%ty : (int show) -> int -> unit] + ~test_stdout:io_test_equals ~gen:0 "print" + [Show print_int, -42] @ + test_function_2_against_solution [%ty : ((int * int) show) -> (int * int) -> unit] + ~test_stdout:io_test_equals ~gen:0 "print" + [test_show, (4, 2); test_show (-4, 2)] + ); *) + Section( + [Text "Pas de correction automatique pour cet exercice"], []) + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/3.0_sudoku/descr.md b/exercises/hferee/3.0_sudoku/descr.md new file mode 100644 index 0000000..e42edf0 --- /dev/null +++ b/exercises/hferee/3.0_sudoku/descr.md @@ -0,0 +1,161 @@ +The game of Sudoku consists of filling a grid of size 9 x 9 in such a way that each row, each column, and each of the nine 3 x 3 squares contains exactly all the integers from 1 to 9. + +In this exercise, we will verify if a grid is correctly filled. There are several ways to solve this, typically using for loops in imperative programming. + +The objective here is to do it in a purely functional manner; there are several similar but distinct operations. + +For clarity, we can declare _type aliases_ to describe all the elements of a Sudoku grid: + +```ocaml +(* small line of three numbers *) +type petite_ligne = int * int * int + +(*small size block 3 x 3*) +type bloc = petite_ligne * petite_ligne * petite_ligne + +(*large 3 x 9 line*) +type grande_ligne = bloc * bloc * bloc + +(* full grid *) +type grille = grande_ligne * grande_ligne * grande_ligne +``` + +In the following sections, unless otherwise stated, **annotate each function using the types mentioned above**. + +Attempting to solve this problem directly will be tedious: many parts of the code will be very similar but will apply to different types (small lines, blocks, etc.). + +Since our grid consists of triplets of triplets of triplets of triplets, we will start by writing some utility functions that manipulate triplets. This will allow us to not manipulate triplets directly afterwards. + +**Note:** All the following function definitions are simple. They fit in at most two lines: one for the type annotation in the declaration and one for the function body. + +--- + +**Question 1: generic functions** + +0. Define the projections `p1`, `p2`, and `p3` for triplets, similar to `fst` and `snd` for pairs, without annotating their types. + +1. Write the function `app3` that takes a function as its first argument, a triplet as its second argument, and applies the function to each element of the triplet, returning the corresponding triplet. Avoid _annotating the type_ of this function and observe the type inferred by OCaml. + +2. Define a function `tous` that takes a function of type `bool` and a triplet, and checks if the function returns `true` for all elements of the triplet. + +3. Given the same inputs, the function `existe` should return `true` if the function returns `true` for at least one element. + +4. Finally, define a function `existe_paire` such that if `test` is a boolean function with two arguments and `t` is a triplet, then `existe_paire test t` returns `true` when there exists a pair `(a, b)` such that `test a b` is `true` for two distinct elements `a` and `b` from the triplet `t`. We can assume that the function `test` is symmetric: `test a b = test b a`. + +The above functions are called high-order functions as they take a function as an argument. They will allow us to define functions with similar structures more easily. + +--- + +**Question 2: boundary verification** + +Let's first verify that all elements in the grid are integers between 1 and 9. + +1. Write a function `dans_bornes` that checks if a number is between 1 and 9. + +2. Using `tous` and `dans_bornes`, define functions `dans_bornes_petite_ligne` and `dans_bornes_bloc` that return a boolean indicating whether all elements in their argument are between 1 and 9. There is no need to explicitly specify the type argument for `bloc` to define this function! + +3. Similarly, using `tous` and `dans_bornes_bloc`, define a function `dans_bornes_grille: grille -> bool` that checks if all elements in a grid are between 1 and 9. + +--- + +**Question 3: duplication verification** + +We now want to check that there are no duplicates in a block. +1. Using the functions from question 1, define a function `differents_petite_ligne: petite_ligne -> bool` that checks if the elements of a small line are all pairwise different. In other words, there are no two equal elements. +2. Define a function `dans` such that `dans pl x` tests if `x` is in the small line `pl`. +3. Deduce a function `intersecte` that takes two small lines and checks if they intersect. That is, if there is an element from the first line that is in the second line. +4. Deduce a function `differents_bloc: bloc -> bool` that checks if all the elements of a block are all different. This means verifying that in each small line, all the elements are different, and that there are no two intersecting small lines. + +--- + +**Question 4. (Block Verification)** + +Note that a block (3 x 3) contains exactly the numbers from 1 to 9 if all its elements are between 1 and 9 and if they are all different. + +1. Deduce a function `bloc_correct: bloc -> bool` that checks if a block is well-formed. +2. Use it to define `blocs_corrects: grille -> bool` that checks this for all the blocks in a grid. + +--- + +We notice that the validity rule of a Sudoku grid is repetitive: it is the same condition for blocks, rows, and columns. Now that we know how to check a block, we will modify a grid so that its rows become blocks, and thus be able to reuse the previous function. + +--- + +**Question 5. (Transposition)** + +Use projections to define a function `transpose9` that transposes a 3 x 3 block: its rows become its columns and vice versa. + +For example, +``` +1 9 2 1 8 9 +8 2 1 becomes 9 2 2 +9 2 7 2 1 7 +``` + +Check (using the "Compile" button) that it can be annotated with the following types: +```ocaml +: bloc -> blocf +: grande_ligne -> grande_ligne +: grille -> grille +``` +Then remove its type annotations. + +--- + +**Note:** To test your transformations on grids (like `transpose9`), feel free to use the `grille_aleatoire` and `print_grille` functions provided in the preamble. + +--- + +**Question 6. (Row Verification)** + +Use `transpose9` to define `transpose_lignes_blocs: grille -> grille` that creates a grid in which each block is a row of the input (and vice versa). + +For example, +``` +1 9 2 | 8 2 1 | 9 2 7 1 9 2 | 7 9 8 | 2 1 3 +7 9 8 | 5 4 9 | 4 3 5 8 2 1 | 5 4 9 | 9 7 4 +2 1 3 | 9 7 4 | 4 7 9 9 2 7 | 4 3 5 | 4 7 9 +------+-------+------ ------+-------+------ +6 5 4 | 4 4 1 | 5 2 8 6 5 4 | 9 4 2 | 1 8 2 +9 4 2 | 3 9 3 | 7 6 5 becomes 4 4 1 | 3 9 3 | 6 1 1 +1 8 2 | 6 1 1 | 2 1 7 5 2 8 | 7 6 5 | 2 1 7 +------+-------+------ ------+-------+------ +2 9 5 | 2 1 7 | 9 3 3 2 9 5 | 4 1 8 | 9 3 9 +4 1 8 | 3 7 6 | 6 7 3 2 1 7 | 3 7 6 | 3 1 3 +9 3 9 | 3 1 3 | 5 8 1 9 3 3 | 6 7 3 | 5 8 1 +``` + +Use this function to define `lignes_correctes: grille -> bool` that checks if all the rows of a grid are correct. + +--- + +**Question 7. (Column Verification)** + +Using `transpose9`, define a function `transpose_blocs: grille -> grille` that transposes each block of a grid (each block remains in place). + +Deduce a function `transpose_grille: grille -> grille` that, similar to `transpose9`, creates a grid in which the rows are the columns of the argument. + +For example, +``` +1 9 2 | 8 2 1 | 9 2 7 1 7 2 | 6 9 1 | 2 4 9 +7 9 8 | 5 4 9 | 4 3 5 9 9 1 | 5 4 8 | 9 1 3 +2 1 3 | 9 7 4 | 4 7 9 2 8 3 | 4 2 2 | 5 8 9 +------+-------+------ ------+-------+------ +6 5 4 | 4 4 1 | 5 2 8 8 5 9 | 4 3 6 | 2 3 3 +9 4 2 | 3 9 3 | 7 6 5 becomes 2 4 7 | 4 9 1 | 1 7 1 +1 8 2 | 6 1 1 | 2 1 7 1 9 4 | 1 3 1 | 7 6 3 +------+-------+------ ------+-------+------ +2 9 5 | 2 1 7 | 9 3 3 9 4 4 | 5 7 2 | 9 6 5 +4 1 8 | 3 7 6 | 6 7 3 2 3 7 | 2 6 1 | 3 7 8 +9 3 9 | 3 1 3 | 5 8 1 7 5 9 | 8 5 7 | 3 3 1 +``` + +Note that we only need to transpose the entire blocks and then within each block. + +Finally, define `colonnes_correctes: grille -> bool` following the pattern of `lignes_correctes`, and then `correcte: grille -> bool` that checks all the +expected properties of a Sudoku grid. + +**Bonus Questions** + + - Based on the previous functions, redefine the function `print_grille` (the version in the preamble is intentionally obscured). + - (difficult) An incomplete grid can contain empty cells denoted by `0`. Define a function that produces a complete and correct grid from a grid with empty cells, if possible. diff --git a/exercises/hferee/3.0_sudoku/meta.json b/exercises/hferee/3.0_sudoku/meta.json new file mode 100644 index 0000000..ba3fa3a --- /dev/null +++ b/exercises/hferee/3.0_sudoku/meta.json @@ -0,0 +1,6 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Sudoku grid" +} diff --git a/exercises/hferee/3.0_sudoku/prelude.ml b/exercises/hferee/3.0_sudoku/prelude.ml new file mode 100644 index 0000000..3b7835d --- /dev/null +++ b/exercises/hferee/3.0_sudoku/prelude.ml @@ -0,0 +1,32 @@ + +(* petite ligne de trois nombres *) +type petite_ligne = int * int * int + +(* petite bloc de taille 3 x 3 *) +type bloc = petite_ligne * petite_ligne * petite_ligne + +(* grande ligne de 3 x 9 *) +type grande_ligne = bloc * bloc * bloc + +(* grille complète *) +type grille = grande_ligne * grande_ligne * grande_ligne + +let make3 f = fun () -> (f(), f(), f()) +let grille_aleatoire : unit -> grille = (fun () -> Random.int 9 + 1) |> make3 |> make3 |> make3 |> make3 + +let print_grille f (g : grille) : unit = + let _ = Format.pp_print_string f "\n" in + let print_string = Format.pp_print_string f in + let print_int n = print_string (string_of_int n) in + let print3 f sep (a, b, c) = f a; print_string sep; f b; print_string sep; f c in + let print_grille_aux (g : grille ) : unit = + print3 (print3 (print3 (print3 print_int " ") + " | ") + "\n") + "\n------+-------+------\n" g + ; print_string "\n" in + let a t (x, y, z) = t x, t y, t z in + print_grille_aux (a (fun ll -> a (fun (x, _, _) -> x) ll, a (fun (_, x, _) -> x) ll, a (fun (_, _, x) -> x) ll) g);; + + +#install_printer print_grille;; diff --git a/exercises/hferee/3.0_sudoku/prepare.ml b/exercises/hferee/3.0_sudoku/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/3.0_sudoku/solution.ml b/exercises/hferee/3.0_sudoku/solution.ml new file mode 100644 index 0000000..e482018 --- /dev/null +++ b/exercises/hferee/3.0_sudoku/solution.ml @@ -0,0 +1,134 @@ +(* Question 1 *) +let p1 (x, _, _) = x +let p2 (_, y, _) = y +let p3 (_, _, z) = z +let app3 f (x, y, z) = (f x, f y, f z) + +let tous check (a, b, c) = check a && check b && check c +let existe test (x, y, z) = test x || test y || test z +let existe_paire test (x, y, z) = test x y || test x z || test y z + +(* Question 2 *) +let dans_bornes x = 1 <= x && x <= 9 +let dans_bornes_petite_ligne = tous dans_bornes +let dans_bornes_bloc : bloc -> bool = tous dans_bornes_petite_ligne +let dans_bornes_grille = tous (tous dans_bornes_bloc) + +(* Question 3 *) +let differents_petite_ligne l = not (existe_paire (=) l) +let dans t x = existe ((=) x) t +let intersecte t1 = existe (dans t1) +let differents_bloc b = tous differents_petite_ligne b && + not (existe_paire intersecte b) + +(* Question 4 *) +let bloc_correct c = dans_bornes_bloc c && differents_bloc c + +let blocs_corrects : grille -> bool = tous (tous bloc_correct) + +(* Question 6 *) + +let transpose9 ll = app3 p1 ll, app3 p2 ll, app3 p3 ll + +(* Question 7 *) +let transpose_lignes_blocs : grille -> grille = app3 transpose9 + +let lignes_correctes (g : grille) : bool = + blocs_corrects (transpose_lignes_blocs g) + +(* Question 8 *) +let transpose_blocs : grille -> grille = app3 (app3 transpose9) + +let transpose_grille (g : grille) : grille = + transpose9 (transpose_blocs g) +let colonnes_correctes (g : grille) = + lignes_correctes (transpose_grille g) + +let correcte (g : grille) : bool = + lignes_correctes g && colonnes_correctes g && blocs_corrects g + + (* support des grilles partielles (avec 0) et solveur *) + + (* + module Partiel = struct + let bornes x = 0 <= x && x <= 9 + let bornes_petite_ligne = tous bornes + let bornes_bloc : bloc -> bool = tous bornes_petite_ligne + + let check_dup1 a (mem : int -> bool) : (int -> bool) option = if mem a && a <> 0 then None else Some (fun x -> x = a || mem x) + let check_dup_pl ((a, b, c) : petite_ligne) mem : (int -> bool) option = + Option.bind (Option.bind (Option.bind (Some mem) (check_dup1 a)) (check_dup1 b)) (check_dup1 c) + (* check if there is a non-zero duplicate in a bloc*) + let check_dup_bloc ((a, b, c) : bloc) : (int -> bool) option = + Option.bind (Option.bind (Option.bind (Some (fun _ -> false)) (check_dup_pl a)) (check_dup_pl b)) (check_dup_pl c) + + + (* Question 5 *) + let bloc_correct c = bornes_bloc c && check_dup_bloc c <> None + + let trois_blocs_corrects = tous bloc_correct + + let blocs_corrects : grille -> bool = tous trois_blocs_corrects + + (* Question 6 *) + let p1 (x, _, _) = x + let p2 (_, y, _) = y + let p3 (_, _, z) = z + + + let transpose9 ll = app3 p1 ll, app3 p2 ll, app3 p3 ll + + let transpose_lignes_blocs : grille -> grille = app3 transpose9 + + let lignes_correctes (g : grille) : bool = + blocs_corrects (transpose_lignes_blocs g) + + (* Question 7 *) + let transpose_blocs : grille -> grille = app3 (app3 transpose9) + + let transpose_grille (g : grille) : grille = + transpose9 (transpose_blocs g) + let colonnes_correctes (g : grille) = + lignes_correctes (transpose_grille g) + + let correcte (g : grille) : bool = + lignes_correctes g && colonnes_correctes g && blocs_corrects g + + let fill_case (v : int) (a : int) : int option = if a = 0 then Some v else None + + let fill_thing fill_down (v : int) (a, b, c) = + match fill_down v a with + | Some a' -> Some (a', b, c) + | None -> match fill_down v b with + | Some b' -> Some (a, b', c) + | None -> match fill_down v c with + | Some c' -> Some (a, b, c') + | None -> None + + + let fill_petite_ligne = fill_thing fill_case + let fill_bloc : int -> bloc -> bloc option = fill_thing fill_petite_ligne + let fill_gl = fill_thing fill_bloc + let fill_grille : int -> grille -> grille option = fill_thing fill_gl + + let rec next_grille v g = + if v > 9 then None + else match fill_grille v g with + | Some g' when correcte g' -> Some (g', v) + | _ -> next_grille (v + 1) g + + let complete : grille -> bool = tous (tous (tous (tous (( <> ) 0) ))) + let build_grille = + let rec build_grille_aux min g = + if min > 9 then None else + if complete g then Some g + else match next_grille min g with + | Some (g', v) -> (match build_grille_aux 1 g' with + | None -> build_grille_aux (min + 1) g + | Some g'' -> Some g'') + | None -> None + in build_grille_aux 1 + (* tries to fill the next 0 *) + end + +*) diff --git a/exercises/hferee/3.0_sudoku/template.ml b/exercises/hferee/3.0_sudoku/template.ml new file mode 100644 index 0000000..6ad25e5 --- /dev/null +++ b/exercises/hferee/3.0_sudoku/template.ml @@ -0,0 +1,2 @@ +(* Pas de modèle cette fois-ci. + Déclarez vous-même les fonctions, annotées avec le bon type *) diff --git a/exercises/hferee/3.0_sudoku/test.ml b/exercises/hferee/3.0_sudoku/test.ml new file mode 100644 index 0000000..f3c1f76 --- /dev/null +++ b/exercises/hferee/3.0_sudoku/test.ml @@ -0,0 +1,147 @@ + +open Test_lib +open Report + +(* find_binding ne marche pas avec les annotations de type *) +let find_binding code_ast name cb = + let open Parsetree in + let open Learnocaml_report in + let rec findlet = function + | [] -> [ Message ([ Text "I could not find " ; Code name ; Text "." ; + Break ; + Text "Check that it is defined as a simple " ; Code "let" ; + Text " at top level." ], Failure) ] + | { pstr_desc = Pstr_value (_, bds); _ } :: rest -> + let rec findvar = function + | [] -> findlet rest + | { pvb_pat = { + ppat_desc = Ppat_constraint ( + { ppat_desc = Ppat_var { Location.txt; _ }; _} , _) ; + _ } ; + pvb_expr; _ } :: _ + | { pvb_pat = { + ppat_desc = Ppat_var { Location.txt; _ }; + _ }; + pvb_expr; _ } :: _ when txt = name + -> Message ([ Text "Found a toplevel definition for " ; Code name ; Text "."], Informative) + :: cb pvb_expr + | _ :: rest -> findvar rest in + findvar bds + | _ :: rest -> findlet rest + in findlet (List.rev code_ast) + +(* interdire le filtrage de motifs *) +let nopat fname = + let rec forbid_pat (p : Parsetree.pattern) = match p.ppat_desc with + | Ppat_var v -> [] (* nommer des variables c'est ok *) + | Ppat_constraint (p, _) -> forbid_pat p (* annoter des types, on verra *) + | _ -> [Message([Text ("Interdiction d'utiliser du filtrage de motifs dans " ^ fname) ], Failure)] in + find_binding code_ast fname (ast_check_expr ~on_pattern:forbid_pat) + +let nopatatall fname = + let msg = [Message([Text ("Ne pas utiliser d'arguments pour définir " ^ fname) ], Failure)] in + find_binding code_ast fname (ast_check_expr ~on_pattern: (fun _ -> msg)) + +(* 0 is left on purpose to make bad grilles*) +let sc () = (abs(sample_int()) mod 10) +let triple f () = f(), f(), f() +let sample_petite_ligne = triple sc +let sample_bloc = triple sample_petite_ligne +let sample_gl = triple sample_bloc +let sample_grille = triple sample_gl + +let sample_s3 () = sample_string(), sample_string(), sample_string() +let good_bloc = ((1, 2, 3), (4, 5, 6), (7, 8, 9)) +let good_gl = ((1, 2, 3), (4, 5, 6), (7, 8, 9)), ((7, 8, 9), (1, 2, 3), (4, 5, 6)), ((4, 5, 6), (7, 8, 9), (1, 2, 3)) +let good_grille = (((4, 1, 5), (3, 6, 2), (7, 8, 9)), + ((6, 3, 8), (4, 7, 9), (2, 1, 5)), + ((9, 7, 2), (1, 6, 5), (3, 6, 4))), + (((9, 2, 6), (1, 3, 8), (5, 7, 4)), + ((3, 4, 1), (7, 5, 6), (9, 8, 2)), + ((7, 5, 8), (4, 2, 9), (6, 3, 1))), + (((2, 5, 7), (8, 4, 3), (6, 9, 1)), + ((1, 6, 4), (5, 9, 7), (8, 2, 3)), + ((8, 9, 3), (2, 1, 6), (5, 4, 7)) + ) + +let sample3 () = sample_string(), sample_int(), sample_float() +let exercise = + [ + Section([Code "Question 1"], + test_function_1_against_solution [%ty : string * int * float -> string] + ~gen:0 "p1" ["0", 0, 0.] @ + test_function_1_against_solution [%ty : string * int * float -> int] + ~gen:0 "p2" ["0", 0, 0.] @ + test_function_1_against_solution [%ty : string * int * float -> float] + ~gen:0 "p3" ["0", 0, 0.] @ + test_function_2_against_solution [%ty : (int -> string) -> (int * int * int) -> (string * string * string) ] + ~gen:0 "app3" [(string_of_int, (1, 2, 3))] @ + test_function_2_against_solution [%ty : (string -> bool) -> (string * string * string) -> bool] + ~gen:0 "tous" [(=) "ok", ("", "", ""); (=) "ok", ("ok", "ok", "ok"); (=) "ok", ("", "", "ok")] @ + test_function_2_against_solution [%ty : (string -> bool) -> (string * string * string) -> bool] + ~gen:5 "existe" ~sampler: (fun () -> (fun s -> s < "m"), sample_s3()) [] @ + test_function_2_against_solution [%ty : (int -> int -> bool) -> (int * int * int) -> bool] + ~gen:0 "existe_paire" [(=), (1, 2, 3); (=), (1, 2, 1); (=), (1, 1, 2); (=), (1, 2, 2)] + ) ; + Section([Code "Question 2"], + test_function_1_against_solution [%ty : int -> bool] ~gen:0 "dans_bornes" [0; 1; 9; 10; 11] @ + test_function_1_against_solution [%ty : petite_ligne -> bool] ~gen:3 + "dans_bornes_petite_ligne" [(1, 0, 1); (0, 1, 1); (1, 1, 0); (1, 1, 1)] @ + nopatatall "dans_bornes_petite_ligne" @ + test_function_1_against_solution [%ty : bloc -> bool] ~gen:5 ~sampler: + sample_bloc "dans_bornes_bloc" [good_bloc] @ + nopatatall "dans_bornes_bloc" @ + test_function_1_against_solution [%ty : grille -> bool] ~gen:5 ~sampler: + sample_grille "dans_bornes_grille" [good_grille] + ); + Section([Code "Question 3"], + test_function_1_against_solution [%ty : petite_ligne -> bool] ~gen:3 + "differents_petite_ligne" [(1, 0, 2); (0, 1, 1); (1, 1, 1)] @ + test_function_2_against_solution [%ty : petite_ligne -> int -> bool] ~gen:5 + "dans" [((0, 1, 2), 1); ((0, 1, 2), 3)] @ + test_function_2_against_solution [%ty : petite_ligne -> petite_ligne -> bool] ~gen:5 + "intersecte" [((0, 1, 2), (1, 3, 4)); ((0, 1, 2), (3, 4, 5))] @ + test_function_1_against_solution [%ty : bloc -> bool] ~gen:5 + "differents_bloc" [good_bloc; ((1, 2, 3), (4, 5, 6), (7, 8, 2)); (1, 2, 3), (4, 5, 4), (7, 8, 9)] + ); + Section([Code "Question 4"], + test_function_1_against_solution [%ty : bloc -> bool] ~gen:5 ~sampler: sample_bloc "bloc_correct" [good_bloc] @ + nopat "bloc_correct" @ + test_function_1_against_solution [%ty : grille -> bool] ~gen:5 ~sampler: sample_grille "blocs_corrects" [good_grille] @ + nopatatall "blocs_corrects" + ) + + ; + Section([Code "Question 5"], + test_function_1_against_solution [%ty : (string * int * float) * (string * int * float)* (string * int * float) -> + (string * string * string) * (int * int * int) * (float * float * float)] + ~gen:3 ~sampler:(fun () -> sample3(), sample3(), sample3()) "transpose9" []); + Section([Code "Question 6"], + test_function_1_against_solution [%ty : grille -> grille] + ~gen:0 "transpose_lignes_blocs" [good_grille] @ + nopatatall "transpose_lignes_blocs" @ + test_function_1_against_solution [%ty : grille -> bool] + ~gen:5 ~sampler: sample_grille "lignes_correctes" [good_grille] @ + nopat "lignes_correctes" + ); + Section([Code "Question 7"], + test_function_1_against_solution [%ty : grille -> grille] + ~gen:0 "transpose_blocs" [good_grille] @ + nopatatall "transpose_blocs" @ + test_function_1_against_solution [%ty : grille -> grille] + ~gen:0 "transpose_grille" [good_grille] @ + nopat "transpose_grille" @ + test_function_1_against_solution [%ty : grille -> bool] + ~gen:5 ~sampler: sample_grille "colonnes_correctes" [good_grille] @ + nopat "colonnes_correctes" @ + test_function_1_against_solution [%ty : grille -> bool] + ~gen:5 ~sampler: sample_grille "correcte" [good_grille] @ + nopat "correcte" + ) + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise diff --git a/exercises/hferee/4.0_rock_paper_scissors/descr.md b/exercises/hferee/4.0_rock_paper_scissors/descr.md new file mode 100644 index 0000000..44027e2 --- /dev/null +++ b/exercises/hferee/4.0_rock_paper_scissors/descr.md @@ -0,0 +1,47 @@ +# Rock-Paper-Scissors + +The following sum types describe the three possible moves in the game "Rock-Paper-Scissors" and the three possible outcomes. + +```ocaml +type move = Rock | Paper | Scissors + +type outcome = Victory | Defeat | Draw +``` + +--- + +**Question 1**: + +Define a function `round: move -> move -> outcome` that, given the move of a player and the move of their opponent, determines the outcome of a round from the player's perspective. + +--- + +The following type describes the scoreboard for two players, with the number of victories for each. + +```ocaml +type scoreboard = { player: int; opponent: int } +``` + +In the top-level, verify that you can construct an object of type `scoreboard` using the following syntax: + +```ocaml +{ player = 0; opponent = 0 } +``` + +--- + +**Question 2**: + +Define a function `score: scoreboard -> outcome -> scoreboard` that takes a scoreboard as input and returns a new scoreboard with the scores modified based on a round's outcome (e.g., a victory increments the player's score). + +--- + +**Bonus (untested)**: + +Define a function that plays two players randomly for three rounds and returns the final result. You can use the `Random.int` function. + +--- + +**Bonus (untested)**: + +Adapt this game to the "Rock-Paper-Scissors-Lizard-Spock" version. Think about an approach that does not explode the number of cases in the `round` function. diff --git a/exercises/hferee/4.0_rock_paper_scissors/meta.json b/exercises/hferee/4.0_rock_paper_scissors/meta.json new file mode 100644 index 0000000..f4ec211 --- /dev/null +++ b/exercises/hferee/4.0_rock_paper_scissors/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Rock-Paper-Scissors", + "backward_exercises": ["mooc/week2/seq1/ex2"] +} diff --git a/exercises/hferee/4.0_rock_paper_scissors/prelude.ml b/exercises/hferee/4.0_rock_paper_scissors/prelude.ml new file mode 100644 index 0000000..a5cb785 --- /dev/null +++ b/exercises/hferee/4.0_rock_paper_scissors/prelude.ml @@ -0,0 +1,5 @@ +type move = Rock | Paper | Scissors + +type outcome = Victory | Defeat | Draw + +type scoreboard = { player: int; opponent: int} diff --git a/exercises/hferee/4.0_rock_paper_scissors/prepare.ml b/exercises/hferee/4.0_rock_paper_scissors/prepare.ml new file mode 100644 index 0000000..13aaebe --- /dev/null +++ b/exercises/hferee/4.0_rock_paper_scissors/prepare.ml @@ -0,0 +1,6 @@ +let _ = Random.init 42 + +let random_move () = match Random.int 3 with + | 0 -> Rock + | 1 -> Paper + | _ -> Scissors diff --git a/exercises/hferee/4.0_rock_paper_scissors/solution.ml b/exercises/hferee/4.0_rock_paper_scissors/solution.ml new file mode 100644 index 0000000..f6ba8bd --- /dev/null +++ b/exercises/hferee/4.0_rock_paper_scissors/solution.ml @@ -0,0 +1,19 @@ +let round c1 c2 = match c1, c2 with + | Rock, Scissors | Scissors, Paper | Paper, Rock -> Victory + | Rock, Rock | Paper, Paper | Scissors, Scissors -> Draw + | _ -> Defeat + +let score s = function + | Victory -> {player = s.player + 1; opponent = s.opponent} + | Defeat -> {player = s.player; opponent = s.opponent + 1} + | Draw -> s + + +let play () = + let s = {player = 0; opponent = 0} in + let s = score s (round (random_move ()) (random_move ())) in + let s = score s (round (random_move ()) (random_move ())) in + let s = score s (round (random_move ()) (random_move ())) in + if s.player > s.opponent then Victory + else if s.player = s.opponent then Draw + else Defeat diff --git a/exercises/hferee/4.0_rock_paper_scissors/template.ml b/exercises/hferee/4.0_rock_paper_scissors/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/4.0_rock_paper_scissors/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/4.0_rock_paper_scissors/test.ml b/exercises/hferee/4.0_rock_paper_scissors/test.ml new file mode 100644 index 0000000..c37c532 --- /dev/null +++ b/exercises/hferee/4.0_rock_paper_scissors/test.ml @@ -0,0 +1,34 @@ +open Test_lib +open Report + +let testint name = + Section ([ Code name ], test_variable_against_solution [%ty : int] name) + +let moves = [Rock; Paper; Scissors] +let move_pairs = List.flatten(List.map (fun y -> List.map (fun x -> x, y) moves) moves) + +let sample_o () = match Random.int 3 with + | 0 -> Victory + | 1 -> Defeat + | 2 -> Draw + +let sample_s () = + { player = abs (sample_int()); opponent = abs (sample_int());}, sample_o() + +let exercise = + [ + Section ([Code "round"], + test_function_2_against_solution [%ty : move -> move -> outcome] + ~gen:0 + "round" move_pairs); + + Section ([Code "score"], + test_function_2_against_solution [%ty : scoreboard -> outcome -> + scoreboard] + ~gen: 20 ~sampler:sample_s "score" []);] +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/4.2_coordinates/descr.md b/exercises/hferee/4.2_coordinates/descr.md new file mode 100644 index 0000000..6880818 --- /dev/null +++ b/exercises/hferee/4.2_coordinates/descr.md @@ -0,0 +1,27 @@ +# It's time to coordinate, point! + +The following record types represent two ways of representing points in the plane. + +```ocaml +type cartesian = { x: float; y: float; } +type polar = { r: float; angle: float; } +``` + +**Question 1:** + +Write a function `cartesian_of_polar` that converts a point in polar coordinates to cartesian coordinates. + +**Question 2:** + +Define the function `milieu_cart: cartesian -> cartesian -> cartesian` that calculates the midpoint of two points. + +We now define the type `point` that can represent points in the plane using either of the representations. + +```ocaml +type point = Cartesian of cartesian | Polar of polar +``` + +**Question 3:** + +Define a function `milieu: point -> point -> point` that calculates the midpoint of two points, regardless of their representation. + diff --git a/exercises/hferee/4.2_coordinates/meta.json b/exercises/hferee/4.2_coordinates/meta.json new file mode 100644 index 0000000..d2014da --- /dev/null +++ b/exercises/hferee/4.2_coordinates/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Change of coordinates", + "backward_exercises": ["hferee/4.0_rock_paper_scissors"] +} diff --git a/exercises/hferee/4.2_coordinates/prelude.ml b/exercises/hferee/4.2_coordinates/prelude.ml new file mode 100644 index 0000000..7904b5e --- /dev/null +++ b/exercises/hferee/4.2_coordinates/prelude.ml @@ -0,0 +1,5 @@ +type cartesian = { x: float; y: float; } +type polar = { r: float; angle: float; } + + +type point = Cartesian of cartesian | Polar of polar diff --git a/exercises/hferee/4.2_coordinates/prepare.ml b/exercises/hferee/4.2_coordinates/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/4.2_coordinates/solution.ml b/exercises/hferee/4.2_coordinates/solution.ml new file mode 100644 index 0000000..8f8d53e --- /dev/null +++ b/exercises/hferee/4.2_coordinates/solution.ml @@ -0,0 +1,11 @@ +let cartesian_of_polar {r; angle} = + { x = r *. cos(angle); y = r *. sin(angle); } + + +let milieu_cart a b = { x = (a .x +. b.x) /. 2.; y = (a.y +. b.y) /. 2.; } + +let ensure_cart = function + | Polar p -> cartesian_of_polar p + | Cartesian foo -> foo + +let milieu a b = Cartesian (milieu_cart (ensure_cart a) (ensure_cart b)) diff --git a/exercises/hferee/4.2_coordinates/template.ml b/exercises/hferee/4.2_coordinates/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/4.2_coordinates/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/4.2_coordinates/test.ml b/exercises/hferee/4.2_coordinates/test.ml new file mode 100644 index 0000000..9a72cf3 --- /dev/null +++ b/exercises/hferee/4.2_coordinates/test.ml @@ -0,0 +1,13 @@ + +open Test_lib +open Report + +let exercise = + [] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/5.0_prime_numbers/descr.md b/exercises/hferee/5.0_prime_numbers/descr.md new file mode 100644 index 0000000..bb95484 --- /dev/null +++ b/exercises/hferee/5.0_prime_numbers/descr.md @@ -0,0 +1,27 @@ +Recall that a positive integer is considered _prime_ if it has exactly two positive divisors. Thus, `0` and `1` are *not* prime. + +We are trying to determine whether a number is prime. + +--- + +**Question 1:** + +Define a function `div : int -> int -> bool` such that `div k n` indicates whether k divides n. + +--- + +**Question 2:** + +Define a recursive function `dividers : int -> int -> int` such that `dividers n k` counts the number of divisors between `1` and `k` (inclusive). + +--- + +**Question 3:** + +Use the previous function to define `prime : int -> bool` that decides whether a number is prime. + +--- + +**Bonus:** + +Rewrite `prime` in a more efficient manner. For instance, verify that `prime max_int` can be computed in less than 10 seconds. diff --git a/exercises/hferee/5.0_prime_numbers/meta.json b/exercises/hferee/5.0_prime_numbers/meta.json new file mode 100644 index 0000000..d996a7a --- /dev/null +++ b/exercises/hferee/5.0_prime_numbers/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Prime numbers", + "backward_exercises": ["mooc/week2/seq4/ex1"] +} diff --git a/exercises/hferee/5.0_prime_numbers/prelude.ml b/exercises/hferee/5.0_prime_numbers/prelude.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/5.0_prime_numbers/prelude.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/5.0_prime_numbers/prepare.ml b/exercises/hferee/5.0_prime_numbers/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/5.0_prime_numbers/solution.ml b/exercises/hferee/5.0_prime_numbers/solution.ml new file mode 100644 index 0000000..98baad5 --- /dev/null +++ b/exercises/hferee/5.0_prime_numbers/solution.ml @@ -0,0 +1,17 @@ +let div n k = (k mod n) = 0 + +let rec dividers n k = if k <= 0 then 0 + else + if div k n then 1 + dividers n (k - 1) + else dividers n (k - 1) + +let prime n = dividers n n = 2 + + +let rec has_divider n k = if k * k > n then false else + if div k n then true + else has_divider n (k + 1) + +let prime n = n >= 2 && not (has_divider n 2) + +(* let _ = prime max_int *) diff --git a/exercises/hferee/5.0_prime_numbers/template.ml b/exercises/hferee/5.0_prime_numbers/template.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/5.0_prime_numbers/test.ml b/exercises/hferee/5.0_prime_numbers/test.ml new file mode 100644 index 0000000..da38856 --- /dev/null +++ b/exercises/hferee/5.0_prime_numbers/test.ml @@ -0,0 +1,39 @@ + +open Test_lib +open Report + +let testint name = + Section ([ Code name ], test_variable_against_solution [%ty : int] name) + +let rec ints n = if n <= 0 then [0] else n :: ints (n - 1) + + +let exercise = + [ + Section ( + [ Code "div" ], + test_function_2_against_solution [%ty : int -> int -> bool] "div" + ~gen:15 [(1, 0); (1, 2); (2, 1); (4, 2); (2, 4); (51, 5)] + ~sampler:(fun () -> 1 + abs(sample_int()), abs(sample_int())) + ); + Section ( + [ Code "dividers" ], + test_function_2_against_solution [%ty : int -> int -> int] "dividers" + ~sampler:(fun () -> 1 + abs(sample_int()), abs(sample_int())) + ~gen:15 [(1, 0); (1, 2); (2, 1); (4, 2); (2, 4); + (51, 5); (6, 3); (25, 6); (25, 5); (25, 4)] + ); + Section ( + [ Code "prime" ], + test_function_1_against_solution [%ty : int -> bool] "prime" + ~gen:0 (ints 100) + ); + + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/5.1_DNA/descr.md b/exercises/hferee/5.1_DNA/descr.md new file mode 100644 index 0000000..217fe31 --- /dev/null +++ b/exercises/hferee/5.1_DNA/descr.md @@ -0,0 +1,34 @@ +A strand of DNA is composed of a sequence of bases, which come in four types: `A`, `C`, `G`, and `T`. + +One way to represent a DNA strand in OCaml is to use a string that contains only these four letters. This is certainly not the best representation of DNA strands in OCaml, but the goal here is to practice manipulating strings. + +--- + +**Note:** + +The string functions we will need here are as follows: +- The concatenation operator: `^` (infix) +- The function `String.length: string -> int` that calculates the length of a string. +- The function `String.get: string -> int -> char` that allows us to obtain the n-th character (e.g., `'A'`) of a string. Be careful to only call it with an integer between `0` and the length of the string (exclusive) to avoid getting an exception. + +--- + +**Question 1:** + +Write a function `is_dna: string -> bool` that determines whether a string represents a DNA strand. We can start by defining a recursive auxiliary function that takes a parameter representing a position `p` in the string and indicates whether the characters up to position `p` are correct. + +--- + +From now on, we assume that the strings passed as arguments to functions are valid DNA strands. + +Each DNA double helix is composed of two strands, with their bases paired: each `A` base is facing a `T` base, and each `G` base is facing a `C` base. + +**Question 2:** + +Write a function `complement` that, given a DNA strand, constructs the complementary strand. + +--- + +**Question 3:** + +In a simplified manner, a DNA strand encodes a list of proteins delimited by "stop codons," which are one of the three sequences: `"TAA"`, `"TAG"`, and `"TGA"`. Write a function `first_stop: string -> int` that returns the position of the first stop codon. For example, `first_stop "ACGTAGCT"` returns `3`. In the case where there is no stop codon, we will return the length of the string instead. diff --git a/exercises/hferee/5.1_DNA/meta.json b/exercises/hferee/5.1_DNA/meta.json new file mode 100644 index 0000000..ffa62a1 --- /dev/null +++ b/exercises/hferee/5.1_DNA/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "DNA sequences", + "backward_exercises": ["smelodesousa/F5/5-burrows-wheeler"] +} diff --git a/exercises/hferee/5.1_DNA/prelude.ml b/exercises/hferee/5.1_DNA/prelude.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/5.1_DNA/prelude.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/5.1_DNA/prepare.ml b/exercises/hferee/5.1_DNA/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/5.1_DNA/solution.ml b/exercises/hferee/5.1_DNA/solution.ml new file mode 100644 index 0000000..12dfb5a --- /dev/null +++ b/exercises/hferee/5.1_DNA/solution.ml @@ -0,0 +1,36 @@ +(* Question 1 *) +let is_base = function + | 'A' | 'C' | 'G' | 'T' -> true + | _ -> false + +let is_dna (s : string) : bool = + let rec aux n = + if n >= String.length s then true + else is_base (String.get s n) && aux (n + 1) + in + aux 0 + + +(* Question 2 *) + +let conj = function + | 'A' -> "T" + | 'C' -> "G" + | 'G' -> "C" + | _ -> "A" + +let complement s = + let rec aux n = if n = String.length s then "" + else conj (String.get s n) ^ aux (n + 1) in + aux 0 + +(* Question 3 *) + +let first_stop s = + let rec aux n = + if n + 3 > String.length s then String.length s else + match String.get s n, String.get s (n + 1), String.get s (n + 2) with + | 'T', 'G', 'A' | 'T', 'A', 'A' | 'T', 'A', 'G' -> n + | _ -> aux (n + 1) + in aux 0 + diff --git a/exercises/hferee/5.1_DNA/template.ml b/exercises/hferee/5.1_DNA/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/5.1_DNA/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/5.1_DNA/test.ml b/exercises/hferee/5.1_DNA/test.ml new file mode 100644 index 0000000..14007fb --- /dev/null +++ b/exercises/hferee/5.1_DNA/test.ml @@ -0,0 +1,48 @@ + +open Test_lib +open Report + +let sample_base () = + match Random.int 4 with + | 0 -> "A" + | 1 -> "C" + | 2 -> "G" + | _ -> "T" + +let sample_dna () = + let rec aux n = if n <= 0 then "" else + sample_base() ^ aux (n - 1) in + aux (Random.int 50) + +let test_dna = [""; "A"; "C"; "G"; "T"; "ACGT"; "a"; "$"; "ACG$T"; "TAA"; + "ATAA"; "AZETAATAAT"] +let test_dna' = [""; "A"; "C"; "G"; "T"; "ACGT"; "TAA"; "ATAA";] + + +let exercise = + [ + Section( + [Code "is_dna"], + test_function_1_against_solution [%ty: string -> bool] ~gen:20 "is_dna" + test_dna + ); + Section( + [Code "complement"], + test_function_1_against_solution [%ty: string -> string] ~gen:20 + ~sampler:sample_dna + "complement" test_dna' + ); + Section( + [Code "first_stop"], + test_function_1_against_solution [%ty: string -> int] ~gen:20 "first_stop" + ~sampler:sample_dna + test_dna' + ); + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/5.2_sierpinski_vg/descr.md b/exercises/hferee/5.2_sierpinski_vg/descr.md new file mode 100644 index 0000000..6dbef42 --- /dev/null +++ b/exercises/hferee/5.2_sierpinski_vg/descr.md @@ -0,0 +1,41 @@ +Now we are going to draw the fractal known as the "Sierpiński triangle" to create a vector image. + +In the following, all the images will have a size of `1.0 x 1.0`, with the origin (i.e., the point with coordinates `(0., 0.)`) at the bottom-left. + +To do this, we will define paths, represented by the type `path`. The act of drawing will then be a function that modifies a path, so it will have the type `path -> path`. + +To draw, we will only need the following primitives: +- `triangle: float -> path -> path`, where `triangle size p` extends the path `p` by adding an isosceles triangle of side length `size` to the end of it. +- `move_by: float -> float -> path -> path`, where `move_by x y p` extends the drawing `p` without drawing anything, by moving horizontally a distance of `x` and vertically a distance of `y` from the end of the path `p`. + +Once a drawing `d: path -> path` is defined, we can transform it into an image using the function `draw: (path -> path) -> image`. The global declarations of type `image` will then be displayed in the OCaml toplevel. Remember to use the "Eval code" button to visualize your drawing. + +--- + +**Question 1**: +Define the image `one_triangle: image` that contains a triangle of size `1.`. + +**Question 2**: +Define the image `two_triangles: image` that contains two triangles, each half the size of the other, placed next to each other. + +Note: The `triangle` function starts and ends the drawing at the bottom-left. + +--- + +The Sierpiński triangle of depth `0` is the triangle of size `1.`, and the Sierpiński triangle of depth `n + 1` is composed of three Sierpiński triangles of size `n`, each half the size, arranged in a triangle shape. + +![The Sierpiński triangle of depth 6](/exercises/5.2_sierpinski_vg/images/sierpinski6.png "The Sierpiński triangle of depth 6") + +--- + +**Question 3**: +Define the function `sierpinski: int -> image` that draws the Sierpiński triangle at a given depth. + +We can start by writing a recursive function `draw_sierpinski: float -> int -> path -> path` such that `draw_sierpinski s n` draws the Sierpiński triangle of size `s` and depth `n`. + +**Remarks**: +- We can observe that the height of a Sierpiński triangle is equal to times its width. +- The drawing of the simple triangle starts and ends at the bottom-left corner. The same applies to the triangle at any level. +- To pass the test, the drawing must be done in a specific order: First, the triangle at the bottom-left, then the one at the bottom-right, and finally the one at the top. + +**Note**: We can test `draw_sierpinski 8`, but we may not notice any significant difference with larger depths. diff --git a/exercises/hferee/5.2_sierpinski_vg/images/sierpinsky6.png b/exercises/hferee/5.2_sierpinski_vg/images/sierpinsky6.png new file mode 100644 index 0000000000000000000000000000000000000000..7101d369f8da6378cb15431281b926aadf3ae0ab GIT binary patch literal 18684 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 P.line ~rel:true (P2.v size 0.) + |> P.line ~rel:true V2.(polar size (2. *. Float.pi /. 3.)) + |> P.line ~rel:true V2.(polar size (- 2. *. Float.pi /. 3.)) + +let move_by x y : path -> path = P.sub ~rel:true (V2.v x y) + +let draw (p : path -> path) : image = I.cut ~area (p P.empty) (I.const Color.black) diff --git a/exercises/hferee/5.2_sierpinski_vg/solution.ml b/exercises/hferee/5.2_sierpinski_vg/solution.ml new file mode 100644 index 0000000..64624f0 --- /dev/null +++ b/exercises/hferee/5.2_sierpinski_vg/solution.ml @@ -0,0 +1,15 @@ +let one_triangle = draw (triangle 1.) + +let two_triangles = + draw (fun p -> triangle 0.5 (move_by 0.5 0.0 (triangle 0.5 p))) + +let rec draw_sierpinsky size n = if n = 0 then triangle size else + let size' = (size /. 2.) in + let d = draw_sierpinsky size' (n-1) in + fun p -> p |> d + |> move_by size' 0. |> d + |> move_by (- 0.5 *. size') (size *. (sqrt 3.) /. 4.) + |> d |> move_by (-0.5 *. size') (-.(size *. (sqrt 3.) /. 4.)) + + +let sierpinsky n = draw (draw_sierpinsky 1. n) diff --git a/exercises/hferee/5.2_sierpinski_vg/template.ml b/exercises/hferee/5.2_sierpinski_vg/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/5.2_sierpinski_vg/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/5.2_sierpinski_vg/test.ml b/exercises/hferee/5.2_sierpinski_vg/test.ml new file mode 100644 index 0000000..754c221 --- /dev/null +++ b/exercises/hferee/5.2_sierpinski_vg/test.ml @@ -0,0 +1,25 @@ + +open Test_lib +open Report + +let exercise = + [ + Section( + [Code "one_triangle"], + test_variable_against_solution [%ty: image] "one_triangle"); + Section( + [Code "two_triangles"], + test_variable_against_solution [%ty: image] "two_triangles"); + Section( + [Code "sierpinsky"], + test_function_1_against_solution [%ty: int -> image] "sierpinsky" ~gen:0 + [0; 1; 2; 5] + ); + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/6_hanoi/descr.md b/exercises/hferee/6_hanoi/descr.md new file mode 100644 index 0000000..b4878f6 --- /dev/null +++ b/exercises/hferee/6_hanoi/descr.md @@ -0,0 +1,48 @@ +The game of _Tower of Hanoi_ consists of a stack (on the left) of discs of different sizes, sorted from the largest (at the bottom) to the smallest (at the top), as well as two empty stacks (in the middle and on the right). + +The goal is to move all the discs from the left stack to the right stack, one by one, without ever stacking a larger disc on top of a smaller one. + +![Illustration of the Tower of Hanoi game](images/hanoi.gif "Illustration of the Tower of Hanoi game") + +We define the following sum type representing the three towers (left, middle, and right): +```ocaml +type tower = L | M | R +``` + +--- + +**Question 1** + +Write a function `move: tower -> tower -> unit` that takes two towers as input and displays the corresponding move on the standard output. +Specifically, it should display a line of the form `"tower1 -> tower2"` where `tower1` and `tower2` can be `left`, `middle`, or `right`. + +To do this, you can use the function `print_string: string -> unit`, which prints a string to the standard output, as well as the function `print_newline: unit -> unit`, which prints a newline. + +**Syntax**: +You can use the following syntax. If `e1` is an expression of type `unit`, then `e1 ; e2` is an expression of the same type as `e2`, equivalent to: +```ocaml +let _ = e1 in e2 +``` +**Example**: +```ocaml +print_string "Hello"; +print_string " "; +print_string "world" +``` + +--- + +**Question 2**: +Using only `move`, write a function `tower3: unit -> unit` that displays, line by line, the moves necessary to solve the Tower of Hanoi with three discs. + +--- + +**Question 3**: +Write a function `solve_tower: int -> unit` that displays the solution for the Tower of Hanoi problem with `n` discs, where `n` is the parameter of the function. + +**Note**: +There is nothing to do to solve the problem without any discs. + +**Hint**: We can observe that to solve the problem with `n + 1` discs, we must first move the `n` smallest discs to the middle tower, then move the largest disc to the right tower, and finally move the remaining `n` discs to the right tower. In this way, the largest disc never obstructs the movement of the other discs. + +You can start by defining a recursive function of type `int -> tower -> tower -> tower -> unit` that, given the inputs `n`, `t1`, `t2`, and `t3`, solves the problem with `n` discs placed on tower `t1`, to be moved to tower `t3` using tower `t2`. diff --git a/exercises/hferee/6_hanoi/images/hanoi.gif b/exercises/hferee/6_hanoi/images/hanoi.gif new file mode 100644 index 0000000000000000000000000000000000000000..b1fa4f2aac522e3c8870236141d3329cc93253e1 GIT binary patch literal 166746 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
m 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 literal 0 HcmV?d00001 diff --git a/exercises/hferee/6_hanoi/images/hanoi_big.gif b/exercises/hferee/6_hanoi/images/hanoi_big.gif new file mode 100644 index 0000000000000000000000000000000000000000..62192f0876161f17d9503d3e3b6f6ecb2b923b27 GIT binary patch literal 253148 zcmeFaXE@sr-~X+x5*_FibT4#|Un^+oBx-dFI&=}#UWpk&1{p|5Y^t?a)ky3vVr#3! zRvltfD+%KMMt{Hmd7l6CxQ_e5eVzAp9@m*1N1h}{9^~Qc`+C3MA01t7h}uKDm3AwC zNs0k44>7SmF|nodVoOD0)4lt9-=;4uE$!xsi=73&+`Y8c@XD>5mo8kpa`Nc$RV#px z4g+s7;m^fZ2|&eNg5$Ni8D)^+hLR<7WzrL?ICy&Yv*B`w!%Z%>y@;OeUgJxZ2@cT1 z3S`y`rVNT~bx70mxe0~Y# zhxQM!un)xHgMtY}k~x+fMxlmB&|E|5(J`@c@d;W{i7#Fzr=&V1rDbMi=j7^Uyvi>q zEQ)!}C@C!~_b9G-`|kaRyKky$YU}DRvKpE{eky7D+}g%|@uj`1yNB4>+yCu5;_DC2 z;Ly{7;j!_F8>5rcGqV?_=J@ktyoDvPGTvHf?yh`4iA@JLV;mCSVe=I(#y~$(-v@zD zJt|HWyh_s1-li3_O-gxR4Mlm7u3wY4KPvp_c@lXG7Lb{z}{gUpQ(H3~GB4|I@EwkPGX;b=3np;+r8Q@^B67bz2$VTg6^?lI4MyClXR!sD_v1$RF`HQ`^7VjT ztX6QuW{>>AD=&>Q4p2P`hPARD)Sa1gF0`#Guph?6cox~$miex&-sZ({Z2UkzBpK_) zbpF&3ucx!!yV$k$OO6dT*1N>L<8no?+-A>G zISxLLr)TJ48{+4pdX)(UoTQ|yi)iAT6N zBJ{b%fjGEH(7~%!kAg{Mprl798{shGnKhWlB%e`M(tS+<+&JW7?Ug5?cH8kUOs^ib zx61vl;o+lF`81wSq7v6Hf6+$ZVT(SOG2SriY&)!3C- zm#?*t{?)kFvTU{yZ(r_)skToS2GdA+I$Pk620PWZX4g9v9<8Xitv~6j=iJcWLvUuQj}z>gG;`Lv zG>=IexRh$|G_e14wYSdYGhcJPYlWD8+1A)Qb7Nk=y3zM5SlP~;wr`5C|-uOVwTe-Z6TBhIWn^!W~;onro?P>G5 zGIumlW(&`6E&1I1u-?w{1)pg9K>p#c@nHhRcJk&0-l{%v4;Y1yeS1xn-%Vy6M`@jnS)G#bZd?EHy;thDt#@|1{Py&#J-XsGvOgv-XWw!Z z?79A;apP^r4^^&{yG<6lHv5^QPDV`<;QC1lZLF$4dXD3ir5^85Z>du6m?XiylGY9s zRZFU0+kja2Vpli(GUUKS@XPtfdn9V07lvM8H(dU{i^FDJZJi8dc|H`+E&}O!I1%a3 z;tuSt)IIO>i)yiM|BN;db>ry9@YGxT*O?588Shyc@#^rCoqMfV(xgdA#@UyLRdwqm z*m<&V7QP?XHL2gKz9%y7uEn8fUY*jljZs9Z#gR1T2guz`(f%#TCzxr_t#@8a_SGjJ z-7WR@{+<5l!S_iJ$2Li|ps|>)dq^@S(=a}v;wC462qwDpLoZLXS|-p4*asgI0ShV&j+CJ!;n4MzsOSrZefP zr>(Eo<~+dfUpa5z;tfrv4UHS1; zyC#Tze--Zu`{vUkcVml{1*Nje6ANgoS0T!2bO+?)4BU_{Uye8rHDNU-TMBmC3LX^arU<~GE3~r zxK)s32f~ymYqQPdJ9E~n5UhUR_TGZbv$H>f@b%w!E2`x}HGi3WZT^1n8@Gl=JV{OI zk~rka`~Z{AGJm5pp#*wy!j&-L;f4y<+yFzL22tLUh{b9r_f z{@$}cmees%4jj&j9vyjH2M(+)9~hRX9JTYD@9dKs*#n)Qq{o&k3@d_eTh_ln6dD&NJ9+gKWZ+PB$llSJbLM&oSvY8^=7`o{ad zS_}PPcf|XotOwpoC)df#Fj*_gC(p<+>iWf?dAa-uCX+J|$$Z*B~T=v?^O=c6V_ z_@*=YFk{=m`JK$hO%2YG#xXzjK*gVoHK)~Wlx$ij~$lgE3od zhA9=dzU-OFlUx`M(a!%oS-<*q$zl(@Iv-0MbAEB?>nI}Lz3W0lusfMQfRrg^%#m!0 zCpx1GHPfj>+YWtPqolDQ}cuBML|E|)+Tk*$^jsxx^tr}XqCdw4uc-Off z1~OgwhlB`UG)@pKAmcmTB(;rB_> z?^>JVCS~n&{(if_=yxf8Chkt#8P9eqqYpCBE6<}JS)!#lezVo+KM;OL8Ez-sQIs?O zr`i7E2!CT^7j?SBT8_Uq3{~6gW%b#6WKvm&8^DzeFjorLkox2-C=kX)>9PX8eh9Rj z@HPQszA6P~>jWyM`dr(Eu|{}5QFfnjLwC3Z+CZ??Z5Xe%(*pyN=1{B`+{+Dy_?8f$ zPepsfaBt_Zj3Rp^!^#PcBQd;GpM_pck;9 zWJFLZJt&d!7NG@3Y()^Ca|o9pj!j_fXB@E)OzNixcfbPoY$p2Z-2cWPjj%~C zV5ArX={?KK=T7o^^#vZR-wRDBhaHWPoe9lyPuddq}pg!K4$ zlYE;QUWW+J8qmL46aHv0_`bZ=1{3#9Y7y4Dq@rE8XQ9*!Osb6k=+gG+a$a<$e9T+5 znD-_zRmd1tWK1nHroKI+jUucgP@Z!Z35>~1wh?^!zcqD+L z64n$atm{aS<|oK1ByLns+-#b-)guucm8ejhxUC~`2S0I_!t*`q&-a-=Kj86PIqLbr z;^&7uo*(5uKdz8;Qa$Mp)1)&VNsy={_2Q(n9Z8z}r1J_dE~>w{Z2Cgm;{`P8#ns{$ z`W-I}`7f?1yu6|Q@|Nk#J034#Q7=u4U*7F_d7uB%Tp{_9dh!#~WDAdEcvP}=aq_c{ zWIKMcgF=dvdWwr_ikn9YA}YnBIK`_Y#fP7QQb=2sf3PH5~1PS5N}&*rD+DrDrT zXXKk^6nbPZqB4q$GfF!$%J~_U3Yl-!GvAwLR(WKyqB3iXGwVAt8~K^d3R$1jvsz5E zT0OGZQCS_uSzR4jJ^ZXbh3tOy?C+-8KRvQJQQ1Sq*&`j#+UofV9Q{h6 z2JYPeiU#pkA zKHK?PbN=;t#r%txH1aQ-Jj7SE1TXVS)8Y(6cJlN2>h7 zu`N$7n45&DY5GOHrtUX!X_vhA_8L`s*NoW5nbX-#D_MtwR_AWKZWtaSBb&>a^FC`}Img%fX$v+LmaA+hJ+bR;y?oE$O7`G;++78@^X>XLx*h4mt6uj5){cq}R4z0H{-A@mjSD3Q3 zJzfU}-OIeKs8L(J#zpF2_=t&$qJEv5%lgPKnZ|1K22K56vf^)};qslvc@dv;wr?J> z&bVsYHFGlQuDHj+vO;E2CClE6qCiTVGWQTy0Oe1_$(?DJZ1Pe9$&vb?t@F z(wei5-gB6*wcF5F(ziYbQLWeWD{8KkW02$VS&=-LOe`6m9`hK`>*aUMmG{{m^XmP2 z|G-JPWsn}ucu?u)RPhW~kCLnJlh~9{X?t%!`of_Hk^o2#2RbRGAP05yq8 z=RL%3`Wpy1yKN^k zD>y9p%*7gvJW`xzvkYf*S3e(g!WFN-cfBw_#bDIh%(J7OzS8P-b!#M&G!{Fc zzSCzm7)sb{Ee_R^)RU2uBjSg)4V$YLwq7_w%DzeOe`w^HTb(^cxq3I))(G7(ApNZH zUB^$nt$O#IrdUbztjFEH`JP2-c+6VUakR0<*Km@1a{Spzs|Q|ndOed$puS)9rqZ9P zO}s`=!Bm1w8Ar#6|0!DVtL}J-`mLroomjz4F1c*P(rCUhC2pwKd@aDeH;{o-vdp zHYTDra(q`RyFhRIWXyVRV)zXi`{~gh^OWvNvi$aee0H!AyCA3omdO2V^cQ#Zc~#^= z7}Bs0eTLz6obGZG?&l$~T9xZxLG!=i?zLK3TLBaR1$$m(xZeBhz3q#yE;ul!DBuar zQ-&ikr4(pN_g;qxjQJLLPu9;2f?2^1G(=ppq+h!R#XMsmZ{fVm#W9y&VQgX8l@l0u zIe(Ip_=;w%4;PC9$Dxcu*cBI3E^V$ib$vf&h@tqFd)E`@TX1zl9Lq!wA0g z;8HpvgA)X%TU<6LG=qtsl!I8QgjNK;p6jzu{MKhSv4=}U!-#DVQW%}Mn?bxHMjB#} zJ}Q&GLr6a%{>Nyf5g2(E8a&1&wSviK8R!cRLXb|RllgSQ0@n|#D!!5#vJQ_EHwh_0 zgecQPyx1XIk)aIG8d)A`V;c6-904pJwvHE~$Rw1r3B^^!J(J-Ot= zCX^9Q*fBi*gp|oHD~jeI5fVv>U{Lho;w-@g1*%58phBfWskcFXA491(tO$BF)E|hj z4!Yk#weY)E!MCl#CHklisoK-*a0`{-2Ug*aq!51~Bka_ItZJw>$g59z5pIJxM-{5G zilHr%=97j)Mp8W04ArlRKw1RS|Gprt^ZyZ~I}7dR3nwE*FfFpuA}cMj(jqJUUs&n? z4475~rORM;w8RS@-BMQE7Ih||)&IFZm7{RHc5ATP=I8i6i@jy|lKURh;VbX0)cN-6 z1|r`jQRVhqy?MtQwARhZ3b)_se|5SUg}&u-Vy4FE4f|%C^lf6O*Y|xjQkPxMoJtu{ zNmqAMzn!MNCH>f|gX*bK(%BYUGAt*o)!tu~%fg&ah3*=Lv>cQ!Bwcusv`@vf#I41s z@e9aq3(lm@wC&E&Yn0)3^9PUB*rw<&vYz*B9(5M8i+Tz`$PaptXG~#jiv@oATb`LW zKulE6Mu0+p+_k)7W};Pjn(lNi^O2s-jW1!l^V?BJ&O9jzWXbjwG8(e%-0M1f__IZo z>$4sF>pp#~YGqs!Fh{_cx~`wl4k5uS?QF34fR!%n6Gblwc25HOl!ox&6Vvobac1v=)vqMD&*aM zMZnkf&j;mxIBrtgbsDWsx>ne40dB~(=8Y%A{JUEAo#NG6MZBVGnXT=xepBceHyLwp ze!Yg}iYDY8!DL($;k{h1T9u#lWWpQWe!aJ9ALZ`MqBrIzt2PW8dh$Of zNEf8)bX(OPRf|lyZvloj4BaW=f628gNb4q8n`S7id(~Kw?cOkaL$9!2?(}rL?U!L8 zOee*ijy`Q;b8J(T6pK`t|1xXjU=gw%b#ErWsA2T3arC=^XN7x39s2rBne57{_PXbm)Y7d&R?9Fpw0zC< zihUo!6f|BkaMqmb`o6n?*NAN#j6bE&zrFR$M;gg0$%s$AYN`G?R?`W8D5n1gP5sMh z9|w~C?qO4-BF0*8=a`k-rOe}t*`q`qSMvPzmAI0w0mHfC&c!iAQ%TQU<6Qat;y8*t ze{$jQ`nT~iV}3@Z{c=s*Dvh2oe>u0W`+d1_=LE|zy=5maZ@y)eM}7NK2-7y@Qwigi zLj?%8&m{m%4`>WkXM1$KzCZn{sch__mS^Fi_Sp@#i(`P5uAdY1j@HehTttln+L6G-G4pRH2*DocnLlA#&9rWpH#iOY=<)Hp& z5$HA{0*kT zVsJ1PfN2r~4;@3?TW)+p1=cEIu{&l z0tRe#FbRxL7g}j-CPX}s8(aw{1VV!gVFY_za0#7I!w$ZhZ9;-F#u1q z=|S~i@s9vZg9x8t#1;gx9Y&%uh_|>z0H$LAPYok{hm(G>upI8H5d@hF#Q{8B047gy z$?h<+W(XOeXLAO50TLttg{)HvIcFXsD<7HwS|g=GT3Zvc#0fdOD|DMx2*@O~2p$Ah z3(J6;M6yGbk>s6HVFX0zJ`<7xQcOjSGQc zz@b3p3Fqah&mh#xX~AbBsW;?<0hn%84l{}j)gBDK*&ZGb3%_F}W*8aX!3b~Qgu|J5 zbF1*jx)CYha4UHlh7qCP9${yKcTk~uSYce`scuLbg--KSqd#QRRCzSiAQqLnN@S%) zR{Gz~O5gbJfN2NCA}5U^7qcR_=y;K&7NxyKX>U>5Ta@2%h^s|h{olsb zrE6D#1rxYGx%yB~`wdX(cO@fUt(oAL`_9tbwc}i&q{d;rHXmP>)YFtUulAG`{MHh| zr2V-35Fn{L;_uJc4O9=ik9&N)^k!C=_U4i1Uyb|EvF1w0EaK za6cB-PdC?ZAJ}U<&SldI^lrLuk}>)y#8ZjxxH%DBs)UA4pdoON6Ut6oF8G&N`s;-F zUqm1^SWZv-{BtA&w2ueq<9urRyo*o)W!314wtzbv_bV{xm|cNcDFIg;0?irjCLHJY zm4OKZftO`5)^y}xk;f-I7bt75lFwH3^Pme8rSs6Z=zA z)A4_Dbu0klGeWNZGwlr)3#EH<`u^nVNKnv~kf1`ua@re&%Yb|R*d^rZw=jG@RJ`PO z+FKb{rtCd&TqvpI;U)zPKvEOg(4YpG@60Ztq^80MmqLi&VSts!b%Ic{Vx$oev7e0{ z0F!^giG!)Axn>dLE)PqE1YdhiU?j}LG)FK8YlzC;gMlSV8O3w(JkM79Pm$y>G7 zBy?*eZpEPAD*4cDNc^V3RdVuSduoE$r}-o|hbqgHTHu7e?O|IZLzNjaD)_LS?W>L= zDQl!CCs{J5+Cxv*tb)i>7gH%`xiaSjHKAHav5QRV0Fs#`<76-hKq3k;GC zmsJbD-5y?x2!8@vYibg5cQD){E!Z57?KvJHZW3Y1i%6qKtbP;0<3ukx zw3FQMG*mmzNmtf;kakFoi)*(%z!9_rFQn+w8xWt38Vl(M295 zMPBnFz!uf1MRjUXomy0<{x9p)BEbIN2H2l{P8HBTe6vubx7;`imz^x=yVNiL<2c1?`R-ZhuWx%oy@cJkz{%ynBS*DLD2PaM=e z`)Jd*%~hZCUm5N8y0qPX^M`W|CmzB}AMLrh;eFhUVKXo;WaprUdIe{S=5r`@rKDsS)BR- z&3RrEYE1At_MKOghTf#BpJD!Uxdx89UPO&q!_<+vF3wdm{wJp$)3oTT952iYKwP_~ zp1$|h*^S=}aYLsXH&|bxc+@8MOl#ik4!G0FZ*}&W2}rv;iUFj+zhn+8PbsvP z3#M~v+eaQfHmNNsJPD>7*qUi*WSc~sHV?lyYUj`INNsS-iRTaRN6dH8B<5^C5T7|Y zD|Sy9<`-vV+=Iq%`8e6AnlP~bNgrGB6}Ppha|3Lg^!|aKW3cB!Jy>$!bvxa0pVz{73u!gGoa)uo_`5$wNN#y4qfX>^+d8Tu z+yrvgc2n4Re!QZRea-6g)6W$bIZ&hGPqfDAWR1naYvkgV6vOE~l4W2CR&i@l<4m^a z;;{L+2MKF9lNY@>0yiq@pxC+MI`@t^kW0FlnrDZ6GNwM-EJ z>^9Hdf-KoL8k!TX)*Za`d^-;56uFxJW6xYd=`#)uP=SKaT0{S(Uc((`2I)@V% z+E4jPH8%++$nnm&TRUgkjM1~z;~ryVuRXn5-dxIg!7R3Cft?NI3)Cun_A!b8rHzJ~RaP+GSigxCZXKM3(RtVZwGkvRr&KLHW{gYAFS(qAFe z->Jy|VxPY@+~qud!$mey2?n&91FnGsZ$JWX!TxV?Z43s99~X*i3j_vE|4Ur6F(0mp zIYF?0FRncSSYD0!TM8U3_K1u5odPF8F^;T1#kCs;Yl!$i;u;i)Ul!LdQgNYhys%CU z6Qe`L-Xe${2vQA;*ux-&fk@}ilP19ASU8CTBQqI< z5juGkFwe?l0hf$|l4l_yWT5|w2yx+%w8TQzwTGB0hltCE)J}wGA02EfuP^#_kK z0jEHiVd_juw}4HN6r)}>AzoCY?lhrlw-caD>MdP@!60>mRQUDw;F~7l_f>+8@!_k| z!p)e$_juv(LHt9Nh#6?a)3hMV$OtDS&ZZ_}fD>WQBZ8#}&h4~77|kt`h=`eTRm5ny)~`OFug6d7m@ zMt~V(ZImb`E{chZV&bBhxF{wrii!V+#KfiL|1Ku(R;2pG_Do=jfuCTO!4eo`+xeH6 zsBV-yOIsh{l%jX9uje%r@jiP8(X4ySkj9MsG2;Af&{P>QSISzQv+riwy^Y#(vK*ql z{g;eevzQ`3Qb>-EvtI9wqpoG6b>lvBoxKkQMdd5(-lL?vy5}w0-gF)r-?TNecO`bJ zQtkwd?4AoB^=aMN3a`{=H90ooIo4vKaw++tLoK15==9B ztms?x=Ad0VPU)f9&R#I<)K;gEIp*VYU>*y)&Gy;Lv(y6vUu#-EIO!H_U>G(;I-?(h{X|K zH_$zKl#sTyHTWMxNck_63!;TYc|ErKIO*~-dy_~6K~9a zQgipZsbx#80d6@at`}eYxv@Kc^Mf`ZCf>=eFwC>K9@}KJbH2UV&NZ+7uAzmDVw*m$ zu&`)!*ktW|eBsYMub9AinW{&JuidF!4frAFl9DdYor2=pGTUUeVVt6r_{ zo(y};|Asvi&1!46qm-0>Qm&q&8k24k4}JXsp}7d^ZAscuM)^O~WYm+~r5mwIkp7EC z>K|Rb&K=#iIH*^Fs5mt?k|KF-NLmT4xS(Oo%vh!=}gQfZUrt*&&u?we+cP?;% z9mxXC@o#O4YX-?$V}iC`hxv%5h6!4Q;BgM2)acz3Y}bZ)RaCT&?^c@YjJu1`dDJ=( zQUc`EJmZ--;JqH^hn_>JR4W!>VFQ+aJ78Xm?uaEN^a`-oP6XN+>vvMsY#&1N01kbe z<8~10cDcZBWCDGH?r){)f2G<-vIwOP4$uIj&#+)uIIyjo#I7;|^w|N1+<&5Nz<3!B{}n=-fCg6~NYgk{#1;G~P_kxW$H8PlD((l2ypo3*25B`QI+{seBLxan3HQd+X938*CK@jjjhFq; zkC#38SJL)s5oC)pu+6b0J2wC=pJmF zz3%o`7dvhgvGaFke6DOlvsbx)=kH-NJh}9Tu!}7xHuXqm@aF|UX_u5wJy!*W&6eC- z{JlqRAj*Ub;f(6I*~*l%(YEam^X3Fx*7)MMf0NfInRpY9|z*UHcKH{ndun^961N8P!nqFke%`5c4M|3wcR$#|KV4CEubEh!RQr(`y zG5*GZ{hNXA_CIv7|2{MB4Y=@-KZnhhA$xh)41z!W6k&>5hxxOMy*zBT0)-3zYau)c zfq#IwP1`T*Vhe}O5ZD)~LBfS_SWqS%a~Xc3U|DIur(=s4!N5W|iyd5);!z1(9yVj+ ztA)d6#8UXpEbi?bh%htW2PdFV!pwLmi|}6c(PdfTLik4*@h6!49Y<=ewrk>$xODO> z5cwCITmvO{8QWh<5O%R!AS3}jq!tvyQ3|=Z+{H$*$ZJeOQ&=I>G@n%}q1)O+-Jqdc z<-;OUiRHLk!yF(p9=b~BCJm@=96bf{)`HZk5ND7`C2IBg*a!01!85i z8LE7MJ|&Mah~~GX#+^#K&|nSKD}H-N;!?V$fsJWS@fXJIR7T3S;iE0UczD%xmb1YK zG}yHHMTBcEt>KxPObPo#&rCo&Z1BJz^BwFvQ?C2nTf)sDU+ zcjw+J`EW2AciXDtrZqDf#DhG&KE{@FnWvs*{);H=6;G*7N`+%=U@51nIvqS>L6_sVb7CaX83 zl)GfapMCzo2;J)0IFsPH=wT#_>iEAz_V2y3j!V~3%X?=+k!{3%tJpIUy^nun=gT>v z$POWwe*fAy_YUY~(}CIVO-hw@j41ccJi=Ww1{TqOQI z;xapcXc?fx@X*D%%?||JLAV1E^3OEASob@*?t$hIcazk>DAm9e7w?C3jGZdx2@Yw2 zbI?n`II-M-?X!qE%s#OAid^B|+21qv{}~VW2Vi>w5ZQk>9uD-f>G;Dh1&A-3w-f&E z3fGE0T=ZX8xI)I5*DUD-`f@LuML_il_s(d_#2W0wOAe%85ON!X2<)8!x46C(Ievi( zW8(}snF|SNN+o}-CSMj4Ug1gtGUL3Ea(Kw7lFuYdI36Cz36W)nW-5pB27I>QL-(kK zd8Pt2?Jzngbakrl`SW2%@u3IsVJFq_2YDz7a2O!6m%x-$?G*8A6nO;YA|SE{$!7(F zl#^1_y~m*@)D|Kt^}r~f*}{wp>v(%GVhwy2>kYG{iZ+M}{;sg^@>lKi0TpPOXS&2(rL- zwB<<@rW{$6wXsy_EN-2JXqCy@KUU~EQt#^8U~UIZGV6G%IiuOJ$Z=QlWDZ5l?&EmF zJ?qSZmq@A42O7pN&d+;%`*|jHZR3D{{K?+A+12k9?VdS3Ms&U%n|*)!o@3yut^F&C zs;krlBNyTeELTFAwZOsah?0dL$H~lkOT(!s;NUgH2q{orQ#lnkzc6rKrnnhvIQ?AC zo6=nVBgyHnhBm*>-EKPFY;g!Css71;1f%K!4Q=45L8ReKrm&%Xtg=;7+Chb|oEc}= z#myFX-s9M4xpgM(TAmjFBW1o@(s!=Z%!_ef5||brcYm`4cCKNOHGhUzc(tf%bvC(|O&7@^hKzlu+GN~kU7Hqv&2`WB z`g(Cc+j>ov_>ORlmih#3GUG3SgV*NdiiJ_bC7jKLJ%G->4(M!}+oGHY{)sQ3v(*5d z4MsBGyN)EF0G;hO=P3jC-oSEOIp7B9?2`j1d7P^x&f&WxTAA$x=SwCnB7~q>jBf^e`Us3GzXaeH7#z$3bgs_PM04Wv-L6d&xSV3AMtxyoQ_4oarEU0 zv`07r4%k2YeehZnj1LE<#SuX?7G8Mp8i9#}iN9FB)8zyYN&|E@9G@@|1fX^WIQX)8 zFmR`AoZRkLziPMKyN36usk7}~Vrr)@H`rG> zpB{W>`JRIWdq)UV;+MQE57>t{AKadwSRDj^Aa0rpIpCaBaK`HXS@o1`vjPoG>WDX%Nc$gwdSylzuM z`B8PKNJ;;xWi`JOeDpx6>e+E=Y-f%!$tGg>+~a3hkM8Ss?nUvVW8OD9C09ZjZ{->% zFd7SAWyy^9N`{j`W()mbBLu+BlO)fDZ@XleEFHsNVKlGp5`PET_DGUUk3T^O*-gOx zE_cBMG5R-TUm*RW3un+C@akVyPA$)%RWO@?!LxL;GO;U0zzkZpi|*XvAt7WZYR+T; zkiDl#y{(Nj{Zi-Q@(h~FeYgD1?W5L~B|Q(u+?4BqVs^Y|_qZW9eZYGhXN>HPmj#O1 zaU%)JrQfrBxbOG+Od2A9W7x-LSVn@W3pu4fW=OmP4ua`a8n^F4lgIOn$J4pT&TYB@ z?2OxVO=UHe4S!xS|FOu zrzvaiLd6OV&|klaId8zlxZ94vY4L&!ch|9hcWis%66Nr zc2R_()3KgAl#y$|XbF_xC3Ey~wzD!F9Z-bc%|Tz3^;c&&Y=`;^`s{@QS?|9f z`=5&02EwfOKi%(wU=oju{cQ&A?;zVA_H66pz~A?~mVLGu42Jph7Vv#dJb#Dxnl2{sw?-F!3scPz69XE3l9OKsJ%gCS2S_>;sc3pu{#9sfteQ zqLYR|*dDNWKbJIt!viP0reVP>hA=a3!zP`V75ePAAkYjX#e^GnqMhOfp-7rjF7il_G|CY{4Mj7O>IkANI64B0mU$hIP4@$|iEbb?8wA%{*zlID~nacX$=iO2-`sOM@?FHEA6kx{9U zQR&R6%=V~kUR17pbe>vtzDaZ;GMW(?UCfLwZI3SJMOVtlyj6>NZxU05jA2E_)G}l0 z+hZDeG0pO^pVVSoOk!J+vFym$4rXjudu$Icwog8;UoGytN!(9l949hvh#5E19yi8| z6HLg*|5A&eHi@4_#&aX%`ONr*_ILp=UQ8ijrFw$6X@Z1D0w^kBO>x4yjs$6bf~-Q~ zM)kzarioiU62Vc43dM=rIudvA6L%>*-=qF~pXu`h9?zAdo*yiJez@cLQU3GeqGQBQVxgW-_{Kx5W`<@pRZ2k9~ ze%ShDv0XoKx{lfSI94!qsA&*X0{ud@!Rpja zfUdJ#G?$tOZ(QPk-lCb3dcu3)4?6!#p!N*)_WB|3LWT9YjRkQ}&pkYtq8?kM>7R`~ zH+XktR1U56QEtQe5sQq-w(GMqnSKV=mS!bl%~g3+Pur(jcRM@U0i?EB&$jlBW`ETl zW%w?(Jf(f@YA~F$Z96yW7OmLBjBs}?@ODiz?8|9Y4e`6_@T)j_ z9Y_Z8ha{;AgXQ*Z7|(xuD)w%$67TC{7UJ#d@*!W3Z5zRI%onQCzYNcK#Hkqb(sT{& zRO3q5Zrp$Zn7i>G#P;v$@y1}UsR!V`Kc~k5=Jp;^^I?4YYkFK#{j_Jo zvcJdB2Ka0}A96mBA%`hVCG`t$W^W&J&Ou(BFyxlxoLl27>}fxku6*6OJCMB4E+eyA zD7FdiUS+$F@fvT&{xYg8<><(8w^p3p5@+Pya$#Kj6EKG6r0V_IJCWbkOc_4Y(-M-K3!6!1VZKCG-(V znf-9|c9t73J+4)aR+vP?-O-oM13hi$GxQA*2=c^#FLNUtMa(T?ZV_{P6sLG~r1B%1HsZr_a#p#(H>Dm1BT!oB0^^APej6#nLMpQ;|aYkuJMmayDQX%uLdggo6 z%qoveR#awfab|r-W+OkdSt09_dRB{RR;x!AJ1VQAI7`IbBIfp!*mO`7CKrXtMPYJL zm|PSl|Bnfii?-4J-?!0bRsL~+{M?|y4W2^&my6F;8U_u4-ZrW5kH|ynLj+T`^$CUr z>BeR7>vNIKdJ-c$VFtsd#_F_}0#3$D+`vvY(A&O~pRsmyVC||7nKy=Kvas822F6T- zQ?&i_Qev$wluTZ~Zyd->Yp^vmi*EQ)eOnK5HtlYMK?+`9i zWyj-S)djYE8=^=p?yvS_-V3SnWMVgWx{1kcLCdGg1n-m5uR`}<+FTitG8_xc9?yIPr zpl|eU6c)nj5Dt(Fd)qReofLsvdG+FTD#tncY1 z&oF^|+8=hzQyweMSQai$(3-qfY+m|$HNv|jymw*fn}8=rE}z_x?KTnk)o3uvUbNK$#OTH~BzANeOQ^rm^ihNAw z#8)v;2xFA0s-3R!*UC13)?TI+E;;POi}XN#_>A2cuo4s z{UPja-=m?8bZ+ftNgoCIA6ND_#-hMVz*(<<3X}ikxpjnRz5eOk7dsHp+m9gsbnc6b zu~L>`i3R@c+!t8v1~>4}-ZnJwAqe|tZySzt0&n*e4v-6b+ZWhae*_)~ll#DM!M{Cs z0s=#XpP>HUMuP-Iqzc32nJj$b@;2HtNDwW2c^eIZ&EN<_{b$zLWr#p()Uyh8zL_B+{*YK5NV7X^cE)U zZ9hejG?zWME1NXVCi^Rsroka!;N%MlWC>)*RTf#m2zkSmTFDFf1`Ron3fXKD8V&-j ziws=?lV#gO-zkS`nTPJd1828G4$!NQc(DB>YuK$slM4m&4C zIU7kjsY^LvMFGO(w!lekK{EwfL(r0<%BxT>^Qdq*RWgKnix~{eqpcmJUSozoRu+2h zG(9mv&kyF4Vn-icF)a z)(Fpi8Nq3OJc7S2{h*cb+?SLTJy?xG9Heiz3KpIFdZ8^k_w~R0+!sh>xJ8CrWVl6! zTV%LJhWkIoaEoYLMBD#T+J1}{`|I4-@0GNkxome&E$`9gb6>zp8n$dy*H@!g?)SMb z$GMWt>wx3hfZ_Jj@oF|(!E5vsOc7~bn~wuan;abnNg_=A=}s+@Li zW|34i3{`8M^nqwLHbo-76dy(8QN$sAf5ojIlJ3D)(|nK@(7$mA%#>9Hl`(s@#1eLVOF?FW=n{hLr|V2SDYn*}Dw}Pp!{PedXVjN&P z+hHVs%UBzMj=PAk$4Y0vgp}up`?X>!GLzs5)j5hLQ}oFBdImla!byk@3Q9TRM{>&0 zJ1fUehid()RZfk8QGc|`0V>^!DmsS)R%-p#k~VH47l6#3OTDC?`??06ZbP-=pkz@i zEqi=iTTV}h&dpM{$_qq{C=va%c*^*=_G39M_2jA|HElnaQA$uzmAlpB+7jA5DzgfR z18Mt;ocXb0>KiEQofWGdjch`*WB>~%i<<~BXa`&6R?H6woo)nsATzEc_7a62VIO7F(vh zQlaf6nyKrF^=VjBfKH>Mr3Ms# zfR+y=*wW)F4cL`}cw1o-jmi@m2=WCVGPO0W@+)pbS9Y6S-D`Js|Dme~Bd#Knt{y75 zdbr{0(V?ry%&vLcT|05;ns3B4RMNFm1=r3rTniYw7G#!j-Yz5LP)1lp20AH2lW^Z& z5_#Jy`S#Mn+rKp4UOs$#rTLxJ8}6+2yt6*?4lMbOUEv-3#ygI~cbv`dZVLW4w#w;+ zBIdA0Pp*l%H8Hm)=GMgAnwa~4Cg%R1bEavk<^R{!ayR2YRm(wVT1`RS9c3Hk=W%CR zg_3is0@SM9eOzd>=OS)0?hZSCZ&x65p?zDo=l=&cdEBlvWF+YLl+^=&= z^mD*!`DcsaYr&TW%34M~33G?vnd4+vI@jC9)I^mV;q)umCEotczFb%FVQ$jf5N0{QGX9^)$)~&HX z#}t9B^K!^S1l}B`V+AF`^QEZSSs+AP(E1cbb;Uvqkj~$s${Is z61Eb?-Wa3;?`M$7;5qk*j6KP@I`vakFXh zi%c12UD_7HglV?%3tVXnkqIj+X_iv$rN~6>%7kUsv=yd__EOp%RNQ(~ZWlGt)|#>b z+}VaJC~GRUH%Yk-aPC$G^9I1(FQo0jXt@b_Pa(WLwv>HH-Z4|EM;7mdka1YbyAGQQ z>dCh&5<3w5ASB%nOuEtBGf19~sUX6X4xV#|VFa#1L8L9fkO)u;0bZII;|ee_WU`b; z6#}dBfjFelTMq0o6(%4PQZuzv6`FJIf-5^4G~k_-GgOc>(g5xy+x*7OaxW_sS59eE3ye9Nrx)||#$7Q?sZo8SIv!|g?$|9`;y z?7t1X3pL2CL2eClYmi%m+#2N8Aotz>(rfNO{;&a1l&VgU2fGq~y%0{{@GfM{v7)$)vbrV`Tor&Z&3opu4)Z*Gq55Zo7GLL9x*e zuvva8EM%3@O{8mv>FZ42yO|ZP-9wj5=Vq}&FL_jzdyECw6msrmkM8`q@qR;`sQgJb zWm)IZj8}CBrw%NmR-LgiAI=hQyL|iXi=I;yvsdiY^H*@Yj81zDONW{F=RO%V2(&S8 z!mkT;s{CXSyvF=3@F9#MeQOXt!@T)X%79I5lYXR;dCR-2c^|$s1>!!MwSMIdGQaPd zLYz0J@p4DVzBfkh8M$D$*c4k6=v@d% z|GTDKZBYCDn)^B9dC+d-RvFyA;HBU@yi z8ix&O0EoYa!jnC2P@N5};$X@;VYRza- zn#ix-$f^4d`$K5R*vP&N?DWSq*s8?R+71^m>rTnWF1kCWWa3J%wL6U|Ge(CG4rx=Q*`teXn=rqWQP-lYDkFb?P_z4kNS$6J5 zC*iAJ%$Aigzd%CFQNF487;6arbbZWvXjHXLckmBF{o9)HOry&0}^UH*etiAJ?7AURU6a@zzJ}8 zTeZ54V0ggk832O=qh(7}i(t_r+AWk?Q;wpFG8s=L+V^G3seU5cj0$ZH0T$q>yVZ4GRI{LHL`6mWa6YY@PC?zD(p+9ju zMm4Ew?6}!#mj_EJu2k8R3^5=_yN{* zuw3p3=bp0W9aHep3feiaS&kHJ!w4{@08J$Dl?t#~iFj*(?n;f#;*l}Hav@kN7ao%U zZVG^7o$xSI`+`)cLGCNtHOM^>kt6>%BloF)4{|4Kcw58U8s66MwuZMgy#2q$+qUg4k5O1L6M*EcqONnL&C!lWFSWJgPDK_1ZMU>P-QZ;NtZprrkIpzYvw z&#@tsvNKIL%s&hpXwL%*)8W7uqdWzUIOe8oKfu-+ug ziuq_FfQ3eb->N~5H!0tcR_9r@-pb{H~|TJSnU zWh@_$xPP;!v4>W#chsKh`FK$R=WCPgjDxy#oDu z^TqF?n&^*S^S+&~1sAxz3O}W#gAw=G*QfIf7sb?!v>LUjBkl!hBc&@!6%xVd*JLo_ zj`aFi#fw(df#&OC$X`BMPM;S2+~!G~&9#Wn|D@uK4+ZQVSg= zulMUN!|7UuD+M|eMEHCg{FQ@vJt*O<01q$2&lW`(0;fODC(OwVn~ByjfyP`A5YBxt zoQsa}9VBdbLW;ues%;n9`pQS36}92t>r z61!a%YAZXk{aCDn$c=qFcE2ce7xZ|v7HJwacDD!VC@R#$$|pUYq$ehA>?fgMp~nHV z#F?xMCrmJ={4H)U#k0#xJ=iI8!fDrtrou zxdHk$G;OxFI^tGca#JorBJZcGFS$hwz6^O2rjEE_%n$^l*kqtjuCsDYSn^ChAEO>_A<4eqtAz{T7wz+{J=* z#oCu8Zc}E_*K}!b%;&bjxCoK1iz)BAEb*h5d(b-0&6>=b&pT{ObCqg)nDQS3yfg5r zM=*T0oM%6ue@35qLTZ3A6)-aSdbRwpO7>6w(fIj-C=4|OBfu+Yft3c)l>(e8K+U2N zg$5*R0YwOK6ck2QVw{v$ov957H4EID1@8ak0{4l3HE(-rNLxeN8q(H~wuZDdq^%)s z4QXpg`~OJV_63_MKGjDy;{Qt8V|Lq5_DHil-t^pn{6^Z{K6z_v13!9s6v-3U(_6#?7jt=xy7mF{aPHizS z{ZytW>VE+Y?VB^EIPUKF&@`w2gN^o(q_5!H0>j}+eMe2DM!32>_UAs&-iAcu(xK~a zSk8>J+zy-aA?2GEEO(#Rhq}^{kw;!QP=S^xv(z81zW8u& z5v)!Gy8>WstW_o3Yvrsja&{XWte11HQrXUBY>1E(Yo)6t<$ST?fZJdk6vru@GrN+l zlyS`T8OGM}7DCF*E^QOrgdQN?9FuTC5kkxcn&uN2B!g6 ze&5C%{p6if@VN4px)6#OHWK$f5UKW=0W~|uKZCx6%D^K}d z)ECjCziRk#z4CsqxVS&BWO(B@i_fJw+0)52BUeqlj4S#gzYq>nIy}c@HAY*$DIBBM zM7|qpofk1M;a0RLyrt_Eq;v@9sC43ved?rCs4?5A2-ou&R{>)7m}8Su@5%b$+FV>~ zJuadjcU*vb;DWF7!>^*?_h#VpD)5gw@$PyAtO@>LxWPWPN9JBHNt(c8N@IlO;$oT`agRBL<|A^V1|A^Uc8qBtD zxa>G|+1c#MCOZvg4`pjG+lqCw^k=>1`GQ>6Zi=y0e8vEo$;TQXc$MA$0Vytf< ziAtk0m~pIQ6m~))m&X@`#skS0Qc}~>osuqIxq2-l^Zezk8#l9aa$T?Ax_j?_-i14& z!lL4mh=S6Gj~+ic{Ghz@>9gmX#8tH~UKZB8s;ie=c_VFl+Z@-}@~-_oy6uCktNUc< z$KJmFy`MgR`#!Mo>)_CEuVQ3Ot5`7wo;&}pW&vy;wpgLTYz<~>Fk6G!8qEIh#%y@V z>bZpH+6zn`RXxUkwU3{Z_5Mfg?QYVy1v{-9t=YP;?)eeX+9S4Sx4Pe1`099xW!4K+cl{-Bi+fjS zlIN~wc;~*lfe zuECEV_EVh0$rGCkOP<}oo^#r>YtQMz#1G{^=UMJN=~pq-2wlFU$i}|UkkXjr0nT!N zbnrPtiFmswAUrSaQ*ZR%Mnhe==%Gn-jSY@ z?82rT?7MT88$Y3Ou*SF5gBg#ad+|p0l{F>QB6Dn8WMARCP-Xao4cOjTkH>A6Y4RtY zsqJyNqK>Mg;lQioUnn^vA6#&sp9d<`Epd0OC3r#Ft~(5td)W8!+@mM1Of#-_-|+2n&0v>@lcIj! zmVvyI=-$$~iWc{=)(wHfpMz869hSv|$~P;9`=>k{-M#`hQtXHuN^4dU@VGvK;jkr6 z)`{!V!{g`U;TgC|vXD^){qkqQTYe57{kH|MKc(5RV7PrqL^>=X9sTW2 z`vj;)Lz9mq&dlEYN1A;esG@UX^Cw&16=I;+irkHCA!0P?kS% zf&!-5Xcis3(+1OQD^iOUs~^IC4b_o>YBU+STcS#{;d0hzIr{~S?F`;&BRP%$dj#No zl(E4yJI9LSbWGJ7XIbe^#>9_^IfnW1NsxF)=lJ>l90;gJ%VL;S##`A^Eu`8DqzMDC zgvF+b*U$;p$V8MV!M-)Y&Y!i?I?*wUjz5;TiAIBAv^FR)T(Kn4DW8jmCvH}7FTh!; zRPKHxV|yhRfh4)<^A2Z`wt{LjTe^pkcS4`!Ddp`}@IWfK#UaF3<$`s{OBxTP9?y?2=!cn_$|iqivJ%$?31DNYRj%!s>P zTd%rbU&4*J_tEL5*R%D5!S~OW-wXUmz7Uxg__P=|rD9H$=v?&^_Ro4LQTf3ytI~G3 z%#AJxt$UMmnvxP-7~Zg@lwmS6vIw*1ZOwIe4R~i4G>bLhtpRThcx%A>zZkrY9%-T6 z?))Z5J2N{NsGNGj0wSFiRDCCPu*laIr^td`Q?5x&e$cte+^%|XPOxLn91rH4p%>(V zVA9Q=53ZYaUIKM@u4L@&@>WV02o3Kg+c+FwuWOeU+(F6oxaqssMf#e#?dD}<)hQ1j zMZ&F;PdEIRo$|CXtLJrmyNY@HaZAP_x^M5p6wi%4-V0w;hTQl%fnP=Ur8Lyv*1eyx zY}EL)pIKu;-2lv~*f`L~tZCWv(9o)50r1P74JRGHJ2s5U!Yw27yZHl)WYMU|XP%5P zR_3M7s=iIb!x8Nr>occRcM{W9w+s$UdSo7O=DZKO@nP@4<8?>Qk+f&On~+ygZmaCs zn6~cSq4f1jCYOET7mj>b$<9{`(&|An7w0Rh!+#f~sq*cMIM{pVb+*gTj0@6MRbbTM z4IXUUIoXM)O#Ge=g7-3kU7bph=6>YkgXQa-%0NL{%LS+}2;QA;T6cX8e;N)dY#yvW zf9us~AKtRy?TpmVk^Q6nX9go%!7;EKY0wYdq%cckcSZKkC7*$@;nrQzJ?FuEdsk^x zhfCl?4jE&85Q6aB)+NLHQrXrQcbLsO{x$gf|q zX?Mn=!CkQ7cN@|=EA(#kRosf|Fd9=x#xUPjF^16lO9!fIO1|bf9lqM~Zuq)UshsAH z{k$Y?NTydjq^wUH9;nG1?x&WH;7U%*?%+xroue|D04f+=?!5d`laf;6a>Gn5nr{c*6p4My??DC1*b7&v+M zEAo#qFnLTMiZbpb-$SKf#}Bpv5+04Dj2~=6l|-6M?Ih0xTjpx;mXQPqofNf`++R)` z2XFW|c%$jp;X1h>cte@1tr#lswxWwf3~+ioQo`6gUj^RTaApN4NP|-<#j5FTAK(O_ z2JcvSTs?x-A&!-bF4H>|jB3c9LB zu84(aaSytZoG@BojXVXckt=xH5j>A9E)uMf3u#C7`KOSv-nM)Pq$=Mg0(@``Y+@}x zKtB=8x1+MiA^w6>Gy%poAyO(JcM);=fR{Bu^p7WD0FIDI%L4XR0&z5sAX7UjU#JnJ zU4bWQ1ZjtI{sV$Ezkg|s+*}m1K@=OQDU)l;s+$tM6ujQ~zRVL4<4!)E;=ayBV+D3lGDW&@u zSSFux>^nhwj^PoN9nB4&!yf$DP1qX*XKj`pGl?N+@5ZRG7yy|d>9)O`hQrz@acm!+ikbCO4W`JwvFjAoBvpp%p>jV zGeN;CHkfkX%Z{kaY5tx=E;jS68NOQIJdj>7dh__%!DjQcj#53x52Ufv@9wn8YuT7D zyfk!&U!&p{yJR4xB)nU@WvH!O*_-FCydT~&d_xac!0^I;a-TPP&cmrlxvNDfE1jnd z6yP4;GZqdNj*ZAYFhdy_+7_JBFKW;Pf7}KSvBBz5@miVqH#QNzmO-dNyfJL*bns>y z9zI))g~B5BMTET-cq12`|K2h=HD>%~JIP8{b+c{7`0LHKjIO%bhW>W5{g*O1@b@>{ zPJef^{q8R}+Ylz0a)18kn{Cs-x!F$n=bLQ|RR!BZ(w}d(1J$s-Unxq|Yfao>gVlm~w-5G$OzQm$!o<5YR@BfbhMgd@P*rYbtP~g6u8W zC*}tT1vkZ<^HRZKa0AQ~KxYYJg~XsP0T_%EtmBDDfF)%|(SQW~Sb9F7V@tE+XzXa3 zo9+K(nf&Cx9=3Ipw6qo|bhPZXw6uN!pH{%cfQee#N=QMIa^+Lk>&38-RfS{6G==ip zi<5ZEeV*Ev`(3L_4nFp-sdC@Js#Is<%l-53X1-Wz$6k6*7?SmhzbkF~V$;xH->?qk zoVp5xUauz|DWxxlhTV`xpRD=ysvzuUW9ZqIr;AO(v)=}U_I2Omhv&5TMvqNiKO3Ff z=0!64S(9sfv;6l9eJ7KAlbml>E^!Yiq3yeHel_oC%9pn^zvzfwnpYaOR}Yo0G}w;9 zlP$I%yF|ATqb^T&oa|3baGQMmD&LLje{FWaQ-5+Q_UZA=OIY}+te;Zpr)a-s)4i`} zXG2ckSYEyTH1mG*b{|cyy~Yy#PJ`MS)YhQ(e=%w&hCTl;E|i;3(fL!MoGp56>Gqih z;`48X^2m|)`9>IV{c-%C3gz5Rd8Sabx@&pMMl8)+eU9?XJz2lw&T|v26(q^St#u+ zIe+&KL8V7~^ww~&#OX+Hh*44HrxM*_^P_!HgN5(H4bczuQu|NAir&|!f`xKrdtCTP z(}FZ|WhC|^uN2gyrDCcCDPK|YqV7bCp%>tG_O%?l?wZ`8hWeuK*`@Y9KJMS7$qxp~ za$vo|rSi95kIG6`Z0;A#``k98rLWux`z+C2x5Klvts3Xpba};SfzRBIgQ61ojMV5) z6-Va2Z5bJ4J1Rd4T1Jj_VuzG_+(uX3Z5e%x$Mt@28heoA^l7jnsxJeFqhLoZBgewS zCy1i7;dsXioE`$_or|9e;0!Y(b(St(Na&B@ufs6v*7R{bnw+*)OpOQiXb5^7j0ULn zXvZ^YTeRr8pom;dzJ#KywzfsFnPNJqNBdPq-@KCX5XO9rAl?HQ;MVq45u-%Htd>#A ztaP5h;$A};6`3j#dAOKaBZ+H9kzKb@-@sV!(bW3PI2pil7ss_oSf8MA8+WUQzfPlB z-DtKP5!)kUjmVh&5S?#O&cuA;0KnFzv4$n=$(8gmxwf`~W1LU*4&uxdvi0*hPz=qW zOM99$UK_~)hrjaB@utWGyeQtGHGZird%ktTa$EW$|Af^@vQ?$_&zQt^Ny3W!#0&^z zjUthUNYuk8Zb35bl$D8aBxRL8cc+lnCZcUM<=sYeU6H)KBJM6B&%=rf4u7S?c?V7T zyQsWFQtsg{9x97^%$k3O7JCB8cgFDjZ3QF||13r@v7aAgn|R(;fOTcepb28~S&sPv zauz9CA)xEWhS>tgvp^SlJS`vKx^f6z0MNxJnF=YkRJ(r7*0yH&>pzfd2mI?%JJwS~ zjucUoMN>7*O%A&Fh;r9#(*^ho?qfQ1jdww&^44XSZF8LHbM&niRVG?>0Xr)-%pG9p zi1Lh`{&d@MQuYz*#ZzVhs6!MBx~?}hH4c8scS$M=Nng6X+?&BFhFtbuU5!G-t)EQx zVPQXsFCTSOqC7a8o^Cx6A2648<U*gt9gZ)}uuI8vBxX|_x(-iF z$`RCg=ThZ`HD;Enca1IZ~|vZQU>ClPM+ z^+`cnQ%(r}l=Rgr&0BNoY&e8{L0<}zr$UU- zYxP*=WQx3XTHoD=Yf=s~EER1&V)VmFCEd7)In$vg`B!WVzqqZBNVWD*WVyNp*rXOs ziC=*KYnS}ejFhV@+({Goe~fLXrMB~TJoquT9h$muw4)R#kW{#ZWq8aTkGHeG=jPwnOuQd7o%^vEOoBh&|ys zVGXB~f66n7?yV^sei!(5@Le{%f0a|z8t9l}n@dRnuQaOMrgUm;O-b!lr@(HHdHwa4 z#DR*w;NHH%sV!bn@(O3=z{jSMDH&Ualn=bZZxob{_IIKsolb+_8Ewk<0^Gqt!;yO2 zcN_d}EquEr-fRz^5ROM>;H4FK(>DCMPTcYNgc+!P$2zCXMub5jx@M5*nbc|X;9(0S zlNQRO7R#b7twP&OwEnCl|AV=qv70hg*!@pk@*hg_Z6J1YNPlz04I!KOt=cRi|E?sT zup30}A4>8FQ7i)eo042c@kb-itC<_@lCOnRBFD$SP^4%Wm9IYHK8m1jEmKlc)koax zWzNOHz+*`-o{<=BvlHW#lVV_4t=076fi^Z2Ja;v3a+8Y#U9?Q+LTTB#bgS zvlc>qXr-g-lEWC|U2=?!`2rmOqLSy;(^Oq@fU4?}i&%SPacy!|A1rQTkg7|57GU*& z<6r35UI<$WiTeW40lVZnQlebMhGelu>Nv84BrBrVYXLde&ij-p}q#1Q- z&rrlq!f-$(dA?dn9wUi&&`S6P$^OYTVI`8TQj%lHODnaP2@^kn`(M(;EC>Ybk_*s@ zdg{c0sS63*SP;9WxT5Vm~xOx|GySJfr2 zq+G!pQs z6wvbH8B&1n&mrmylk^1?TOkcewKGn-WSevanRE@4lqpR5H9zTwG$~t=lxv!N+cx4j>=g<8{;m$oUdktubU z6p1jUAwQ)_n$oODX*ErKXPf#Snc9I#l?hY3^HY1IslAF+Wxr|K7u&RN$g}}Wnp~JR zl%F;tO;ak;w9L{c+NJ9pO4p4@ha{y>E=Zr+kZw4XZftgOy4}T@hc3>JxCl+UXjX7> zPQyiup^Nj)F8yS8Y0;reOCl~=C0$xtaOszZOUs8Ytu(v5+V1k&LzmY_T!tlGwkx=- zaiwL;lCrxCvU?h`d;hbpv~&MP=B5{l3^mxT!S3ECkJx?Dj?te6`aElLyzG=?%IZwew85*}P}x zDxPAep1<=Gd+J#*(`TH2T9^I&{Qc#n8tfiXXtyeLB&GFI0?u;d`~#3o*x(yBcGaF% z_#%ky*d9X6ya%n#P!z3i6J~lp zA)CEuU3kdq(dU~h@lN&^CS{~Qn)7bu!mZEe7zCf+@^GC7yZy`|zy8l)x1nk4eNolT zfN<63c37UxiL>iTg}VsK0g+g_D?5N#h_sn*VlOa=J)g1TNNe+*eDvdNi#X4>y>5bh z;fJ2!_O3%#=)ofKq?|?jvW`sOW@0Rh3k-a7eg6PCkHF&OV1swG>|GvO{OMUAcCfMK zxH7L~Lcn9Ze$?Ax-}(oQ%lc-_zR@y~^0iEPvORLl`MBZ``U)>LH*!)_+gB_sdx36{ zolOsA`sKmLnY&iScC>D@*XS=_?2pT+;d(FRJ>;PdCp+Ij{{U8 zhR->B=Bhn+NoQN->nG=2{cW1@+a>x4i*O?<8In*idgm(7$F2jXE2CVUa-rTly=-RbcVRrY;rc2g8Hn;Du z75;43%{*jlkPdEcdlaMg3<_&^==RJ*?D}}f!%lEjDZ74^+Br`$R9|-R?)533I;Uti z7(H-Xf0HuzbZ74&M#eVc?TnODzLv;4kE=UxWL2GY^GvS#@O*vF}2VnAW57Y*T$qiNjg%*AvAq)q!D*{ubCSjOoGZvk8Iu zqLpbVWto5WndEs5XKY(!)fkI2X)Wfj1a;qPY0A@=pEa?*8g{)D9zC5w{MxSXt3Ba< zwfclx)hbt=a3>e{{IY9k?dlYg_yX7}SDkP-f4{3&{A+k=_p2=D!II(8zH@nzuP-+D zQG25&wCH7pE0(@tP0I69eoY^X=rHQ-snDKv{i1uc z%msJ;%xUHGOpoH8pKFFFKMTKoVvY8M8_65IMh0%Wi0UxzBPGKYAKS2_$rP`V4xi?0 z@$-J}tf^nt={eR@kt2%c;W{f_+CW8l6HAewEJP#6m%vVsFM**M+rj&7 za0v{c-$dxBTIDe2S~x?5W{TnT0*Pt~ELzUk;>>&ri_1kZE75Th6y=$E3GAc{nFNn} z2V;>@aZ)r(CW~#9#r23;wJ@FcQ1+KhatFW~K(n`qSf51fDmiO8OtrafivYuKPG_dd zopxQu-Z-CQsNm48baidx!T$r$oyHe)oVDWTSaWn-L3bKsHt0@MP|OtCbA<_C{_IRbwd1oBcV9ZX zp1rsCDB~=zK?B_I?q7rbB0di08g5vbR8!6Dx!*G3s;v3s<->$Zm!|{4=Z+=!Z*e2=2>m&c)wCO<>*8mLE-)m z!?RsFDBZ_NH6gwhQC%*_);j}l7j-O-Gj-Z;@#=ywWbvF_n%&u$s+5AuzB42WH-EDi zsoPInGydGQ1-oflsvg&JhKx2FUtXQQZodY&HNdR_?*Cihz6ixP)Pv5n!XLoxHdxR& z+IueJ%xePu%^eW9@kT|hg{fgh=E{C52;2ragIJeuUHhR$?=9UiWhU6q;PiHf&DcPB zVbtTZnME=e_lSvyQ@>`q7k78{Dv0ei->$NYdq%NCZyJ>YS%IUK!l@MLuyXKNjzcfS zJ?imCWt&x>L%(1wvdv^nQCm}Zf|&g(VAa^r?Qrb3ywcHjFMCC9MTz|9oao-U|CFn>((Lb|P9~gZI_LjfUeHF1WdVxKqw} zLs*zD6+c~!9_vK!tq(T>2uDo_GbDr?9)z@TUuY&_k(gkCj!Dsr$*vDr2#+yE#aKa! zr!w%1!(E?t#;g$GR*Q(iF2rRaM3@Y}QcSdv5uGST6V;jb7I015FV-a^WLajc!Y0;@ zir)>vz-M7Wf7(toX%|4M55^slk%F5N<@@pt+A`rrZ&P`Q2)4*l%Rt-F^0|agqT_%c|Ru|U`=G-!RKTPFJi$kyr zDM24aY+VdTAYp6ebEeu-C(@#dTy!9|@iS?Z$x;qf!8TGvPXmkPium~oT00QGsFGuz zg>CJOUyz@mhlyXTNT^3Ad>4dnDoa=|O<1U(XoqC2&d0Ac)v+< z?>wKo)071Y({>~2a0PL?lzY%L&fS!ED2wsbnT9w<1JAf0s?WHYFrEuUN7XGiWt{Nm z2VtlvQ_^+RDzICgBj=yX7sS8>hfp42V7I)I7EvjP>=O7uxWs$_KmcTY0X3g|pc3Hb zQ&W_(P2)_n%lY;nai*R6_W(CTqfOIj(=^&NjW$hl%dP2^|IY;6udr+G z3I3Nhxc|9Vev~qyV`rybqoNuOI?DTg=~$VF3=T3>-c8rrC0q9lQ$uyTd+B3S=L)A; z6{oE3Ua{QOwPs$#3!r-K4u>f-dyb3>sRn%*8SV_wp4W=Ojf+S#=4if z$Km9G!3S?dy6bM7topc1Wh2)K$)+5U9T_aBzni+!d_jt?_Xf|}a>x5O66bz6i5rqi z0)p>eJ$vdDs5Y;!zAt)FbT;60L*uzweir3by&j1TP2!HbX%5BRVPI_Be{^lZp6dbj zlHumQ>hK$b(T&)aww8D0d1cdf_0J}ow^bhmi`&ZrUHTHGC_L=`V#jm#Hgm-GR(Hx5 zTJ*DghT7re*jyLKGpyvM`q7@dGxsUA`7;_ntVmmxX1FaK*jhHQyz#l!Ij}iaznqeq7hD zl#4C#eZ-oPj>aRmcPdY{XK_ZlOR(jgI4~AMz9IWoTE58JfHP!`eme(6$ zdIV#mx1DeW$_F>G1C6*Z?UYg_ZUqkeD$M{?rJd-+4twBv^|(ocxN=MURC%Zl3U3TY zLu5hU>+!QQLye#WGpq0!kn?)RrY6uBgn(dL=kZ4ogWP@(Hi8-jhTeas35;%u{HqC6-x5Yi=8YXx}jrt zTaos{N#Hb@2bGkTPVyF$n)P%h1pVeH2LtYZVuUKa|t&i80XiI>i7Jbd+=DjB(zMK`?&}Kex3^b<5o#CReL@`yo6oFE2tQQjLRE zO5$37tO8B7ka$#T|P6Sf$k3b+%{>>&h4w~{hK#h#RFPn2>D zttmPf&U6}E-xhboO2;?nb1ul+jUaD33ArigL<{QFZ3^ylDtEgrk0$0? zBRse0^A1%q_PX+px>66?62m6ZKyBI$3C~N&$3gk_@&sQ6KLd28*$O6>@wWhpK|(>5 zBtAqc7>06LRKy9eS8hr>-z7NeFW4#3CP{&CG(ffG(QV!Ab_@9!At2Of(=^(&|D-m} z>tD&+6EvZ=Ce+r1+L};%K{aeK%gONVy9DLWGcg}pFT~l}2m-U=ov8~aM#v<4+=iSk zr&(x1ZJle=ehFMu1HBluZN>d_gzy_WM)89WA&)uiHHUqMBcKmpORlg;5}&U_5Q5Y4 z)tsl=SB#FpoVGje=KQku%0}ZZV>iQ1@vnW}YtJ#=4&PRjKze=|Zf{+&ten83ygsq< z7&5`uk-)#hU91VU-K*a~0%lQ>&kofe?7o9{n|sX5$5wJE=8bXWk<;Fd1(Lkxdl1Z% z9X{U=A?io(5q8cxbF2!6w6W(y;y)br+vUR5D42Iex?Tc#^)$@+rUgC125rpj0z~--+E)E~O z&SKVamEWVCqPzX(Dc-c0)g7f2_dZ%4*)Xi^y+5|DSDFhha2F4jx;r)y(>}F%HOp&r zY`=gD+&9)ZHC2a4f6H6(waa5{cv~s9Ah>4qOJ37(t2?gtiBVsGaBOt19%)F~i3=TY zIXoqvP^x_4KBjDMQqlzQ-V9t{1y1RL)1n47xZpqd;q@}H`tqRn75GMQej9?FX5}^o zPI%!*FhN9{S_PPk3FV!Hd6`iQfM|c`m|0dhOL>eoC*~$d#=D8@(L@+P{Jll)3ysx zVVVt$5-~1J14vQh!Zd^y5w1i30oeeN301eqCDcT9i`*NaZSJRKgODvDUqsOFf}u8q zl#xj<$fVz}(z&G$we4VxJa7mMPR|FAw-K~rsLn$$)Rxm9qnWP}acF?~42Z)@m}{Y` zP@4h+&$(Ie;UqBBmchtPa_v?Lt5-~ZhfoRAwxd~HX!cPN%dwv|B4Im2*`P439mSS| zLtqkZCBT^sdeXoaxvOfEy9>>+&*jYSVrwZlW=O^~>-YwEyk1cJPhBk2{P-o-^toN} z&maj4r3uZ51WRy%8!~w*G7+qh!%Ugr{Ih)^RV*ND+H7uuS_xQ4AieBP41|D5utJy)uyA2|1-+k zQq2C4Ro^S!A4vA48Xky?k9xnTQh!Ux`ZXCn*1y_?*&WE*HtW|Fx0c#vCK+b?&bsc` zf6Dgpc9Warb(!$_A7QVT8s2AZymEPw^`XMB*BZ8d_P&3qtG)M(_L=3c?%r}=5Vd)3 z;LIluL933VioAc?H|=ri_j3n$O$Qd1A|AF~$qigKwP|1@<_eGdnRstb+f47xFV?TpLV|jF)Sl2iQYKUq2B#6!JbPT9_e%jW#rMU zFM#=|4DO|P-j@6I8Ym-&^(ksIRBT<`zSSL+k!Odv`pi=Ae(BHueqF`ZCYZYX>aZ&- ziVb|gs6FuLz+JE~?&lTRIH>%RcjtI7SQvlwku~_x+(H)7S@`Zi@8CL!@^g&kNSmd* zB5{V#XP_^_QD~^Bt~m0AXA~`)k)|lNIckvV9xd6pVyMnM_1jgy;jUNdLk$~JzGv4& zHyocge6z;F;Jy*I`drTNJI|ECV)voGn)xFg9v9^0?2_8}w2{jzTBd>l_T!5yMthSl z48371zCTMJ{bF-rxY->ielSm25}DF*aAoO;as~_gy)mu5C$RK+ON~MyC{=<1wl)U` zPJg}8#T{tHb?D(IqHsD0ytNJXL^^&|8b$M4zSOGw7r=0M`0WaZgi7>53moR$>+xl<6?3MLMQsS0DH5XLLML4MkvWt ziF#xkp~Z&l@W%seL@Xdtvo%0VgQ?g$072iJOD|AeXj5)N)d9AcbO*&KM=?ZJI>jO; zc%i)w%&@`TF9_oanpp>>S3-5FMR83cMlCF^70QZ5s7AKwK%8@}N{q&p$EilPA>{Wc z);Bp>Cf8Pp(WsPOsH!l25XSxvUDGBSQPhUTpv z*)}$f{|<|{2iZD{GZkcOQzppP9gqaQl?ltU*o!d6HU+}~J1_d}P~G1kohf znj~A3WNVUaO_FV_p>2!TX-e#tj13i2|Fas$@=vPt)HNRjva{!hk-j~V6cd*EpN zdG-DqQ)egcDgA#(+qf95KWWH=Sa&XT{^G~C6SAB;2prRmosPqb8a!ARPx_>?lLODg zJMN^LRLQmt3u}FD$FvNE=T}DyKvxjb^9$lVsb7S|x z!h)(~<(GX%DK!V&+uw+=p*Ma$b8^L3v$^?(1DW9tAC8*rt9x&J|MrSqJ^FXz>-O4z zx90CUyUk|9)0)(^r+z;2DPT;|fGD|gKk(Vvz~sW}YpjsF7jJG2-O=9EPhKOkUKZeG zVlMeyJ($#4{3&AIaQ%ncys{V11J4gfUJzSUlubI?KbuupupJ>PF;DH(<_h1w;Gj#a zQ@a}u&UsC*MHkt!K69}4FD}kjSKWruuywsnCZXVYwqDD(J0C~;PX`tjr<^r-xIy`u4X$ikvO}MY4L85U zgXh^R!YlVFznJ(HIA=Y)?vy>b7hi68Md*z3+}K z09UsC{ggLCYZzY&OM5z9aCOQzrS6@5%2ByqztqJc z6mlqX&u>X~gorX>CFuk>{RLH@X9FZ(D21w~?b9&I)-noboVKlEu?Q-lrtK4GC6%ex zkWXr*ZrM${rl##JXi$_!2a{|-1}5B$yAtvhn9i^2x;R=)&s8Vco>q)|2*CKvbnp49^X!LkW;r4*oyy!;%WM$E704JbRp;5%*QmJn0969fX#!Z?Fj_N|)hlNm zgRwrq*a2vkQ?9Bm9xh?^OV~;j=?k1aQAi$;X%C?}ko?#YP(!X@O|a#FD_;&A&P-%H zU(PfT#w+C`| z>pXwQ$(mGYJk^#9#=z{5+&ePPbR=^o#%Pv;tA=xD+w!s~T<1W>&mhSbGj&O}n7SCr zE7vF4kEJ|ak{t(bask2`o z-8S&{|2y9P2r*WTrrSE+wkU=g!wa4Y|Cs&K^Y-+P#IT4p8$Ha<>(8^@o@VR(;~?F3 zjCvUXc$*BG<2LRKo{s9!5W=fB72nlL2d8%(tN_h%AN1wyI*ZUN148B6<`-xSnZ*r-k*m-Obb!aM9= zY4R(yEw?~(eBbNAi8D)7Bi^xJ3itIz))iE0-BXEux%<9!*Nmv&*7U1G_Dt+=i};v< z9VZG^Mg;eg$BltT7$b0+2%I_8{~Ode3kX`(hJ(sNtyNg~#n>s3@ChP(YaV`%P3%lL z+Quo)79O{_4ey8w{Z10sP!ZR1C~h7i6!7+w^#n5;;3X&gcf7qGMBDS@{~O-cMcYTv z|K&V82t?Z^e>BG2q4E*DC`%2C`CCAK~`wNxzJ(4@WlIBR|zL0R|V0cQf`IXPhfs9|Q z<}Ic2R^=zSRf5?sa|)Q=z7I`Ytw_vAB(AsR?-zj!ZThBm;2)=Lapv!?jQ~z5~P;NIDMk2{zYC8Zl$Hf#(KA8O?#5<K1C zK^^+_`+LTflXnJNe6#GJ$!cWTHth#%_Lalyc4O`!k6aIUU}WdF=V>B4?vDh2wO@(cp40&0*15PJG z+n`Ift8ZR%0ZU&YNf&V*`-Gpq>DYkQJeTN}zBUJLIq#Ahk#!=|`A)~K2F=UN@Fgii zulLiljqA!L-p(;k?_4wD`f}H)1+z(2AC8V)9K^8hu#XRRf$cTPln=#-f^d@ zC3yd~)lt*lJNUZZ$gK@Vmn~~u8WW~n5x_J#ysxEorZE4}^sQeajD}lQMTC~w?EQG& zeOY_P;)q~=b8kjlbltOe`I0au{j$rnj!m_9l#x{ul8$NVXdX_C`EB zkfI#BM^D z^y2X^S~NZv*&E?h{J{*X7mr^KTK>$}GRpn2b|6qM9v^zpS@Ldk__!Y7zHq&GJa6z* zL)FK1^G7t*Alc6IjEVA1`=%}+wm;8D+Y&L-G_ZG#O6aNmT-em#wjQm~PPrc2oHsJ8 z>W&`z-eY9V^O1o8kZjA*gBhoX$&eA3s4*~{r4pSs5TU*hZH|bvkmIIOB1~Yx%`W&0 zzIYoIK17KBnLLBLe%v%UUSt;clQd!$BF<(q+7T7$ER9?26k`R!e1nQv0LRQg5N68b zify*8gqyj`2#b^ixGH>wBvk85B-RnUWVnscFgGPJ&Mbbb6K*>+oZugSGlGaj5d6?y zYpws0wEqx~D@h;{2g&y6(%0`ja+033Bc+xed11n^-;!-0LW3G%B$Th>aWJrrl~Hp0 z) zJWjb!A(W%(kHFHGh*9ih`b5rXgfgBfBV%@w>cPM^C80{C>yf8HnS}$9@6fs%?F_K4 zts`wp7bTiBmDQ_a8DYr%BHcxHw3PK+7`?`tZK+_NhOtd;bu(X$8SK@~Y^a!XRmPa4 z;Fx%ElmjuV(m6l0v#r}XPUZ{;XY4H7aX>t7gyA~7^2(&#p*rpgBc7WxYoQmh|r|^9{E{`B?IlLOSX}$ zX&~9AQuezNSA-=1@pvLdpi8zZiEAo#BD7ewFwj=?E1CmDXklm(rJYZs1{p+X=T{hR zwEz7B+yA2?v=PIxwxK?5sE-@!l@o?7Z-0aE9N!ya1SGPRBarDGK{}I|Z z&r}L=$j*E8x+}7-FnQcZx3ir*Iqhwsa&vZM-IdopNiLjsYsUsM?lfxZ z2TERs&D;Fs@~h`v#-&CR=c7Z^dzXwKxIX5aoH(1^dxlf{B#t}oMbqyzk2jXzp{2Cr zLz4Y1qC~~ey;-C|`m2#x)qUlJ>)1~l4&3={{9t}p7H#9>7Fm{R#)9h8;Q=2GJU$7H zK-OMo+iz+fUIokjVR7&>(l3W*Ji9INWOn}`x_Z#j$L8Uj*w6ILX$OD)9-6f_riYtv zd2n6`^fBS_spYcWt+VK^m0Ovo=E)NdEHZO>D z){5pYFs%x3xuTYxUiaA@dg9Hhjgex`T*$?rkGFSz>@V+^FDAHOYnbm@Z$AzWARE}m z3pFotn76J5Z0tbSs#cJVi-hH1M7!JIaLY~Pq;pIYl{1DbFBtb=TKd-;R8J&s|^ z#~sdVQOK)r-!`{C@#8()8oYPK?TXD^`yvLbct*YA7hAi+3maavdGzI-*mN+s2q?yx zc@*`=?ioVkCL_nZl9g56K6O#k#`vIbo5l9TB}X2#_PG+0>?Qp-U)T=rek-Y4clZ*7hH;tc@ay9S2W2v?G%x9cV9z4^~le08R z^3n3(fTnF}g*s(>>}US>#qT#bx_MlfIPPplOZyU*+l>h;!+PQFwg_@$EraPlW~ol42oIHGCdqM=WjNP1oGmm?l!u=w$G5uR zYaQZ#lB0i+hC0gdfUp-zBhP$`Jv9)!CkM-%Pgv#@D;GvRXv0_3881f=@pW-iB_~(Q zjMpKEa0+2H2diEa{h*e(Tt(dGw04Ws#7z#_DI*|I5i6{X4k1YY5NCgdY?+R*(IWD{ zglx3bat}0q?G`fgw>UcyY7{~FS|8WVX~#$?OA(kSTd5aD<7^a3sMN>VDjEesQ?$7) zDV zi86Yn(>OU0p`jBV$#rpdii+`WAo>A<*#l)=L^C@OtbRD<102^Sr+fzWaTTQ(%^K^< zjuf#5d}CMku*T=JgP`njYIZ-0Jq8lEri?um!?`SBSm$$$ojK#3aGp5MtV;I8O3v)| zgc;O0hjwEiLeuI+XeA=)-A^dIm{5Uw@Phuj&@ec&jcUAI_sHqQgD7#Vog9-s3AqcP)QbmHZ zkf`HePCGNf&RH0(;D@@>W32@kSCLV^Fa{$+i7k(s3nIKkOa+@n6>(jKG^FrJh9S;2 zh|s$J!{cnjuGde7INK0s8{%w3oc*_tvkjzeAZ-I_8%X=|BpEPIE73b)uI8?261+RG=5iYe2o9sIGaPErv0{~jfK*XlC>+~ z7NdvSnX<2O_FWjA`jVnB8(*X^j-OV3-Ss*Hr$YlQAA>ksmH!Q!OTnIU_Lkw+hY-BuBJ1{yK=WG0bTo&JJWdjRYLyNGX4=HYrm1;s5;?*_|PHoctF~rO8zmVP%IbJM9{wnr0vYM4iw@P zG<5!^AnJHP+Cr5OpD#*=iB4t+=K|8kkU}d(f_CyviE&cCL5pTM)c&^}Y6tvJ7suVD z#3(~|+yL7K*fzkn0k;44uzlIk9XE8x4c+no`|kMC&Dtzm8}t_F5Xthr9IM zaWJCYxx90AO`Dv&0656CJs{S8)7dpp4ya$Dwq}_v^pY{X?J_oUO>CndzpCK7}Z|6Yr zEr&QTqJ1Ux%L8zw9WkP8Tz^k3uZgWE&l`TV9oznrOzN*li|L9e?Q8NytCCl0`xiA0 zwT(n;w2pIo4ogS0x5(HbcTX&{ZlA5n1}DkD4i2D)9m7Y2mSd$j@p_zz9Bl?c5F9Nn z5qKJy_>x3b*r2!F54+@#caWlMh4{~Tcpf>LU>)}pGy)2bOBecpWV^aPnRKzSm0#O$sGEwAR7)6Bwfo%4~r#aUzDh zD(GIb4z}+&nZ5%Kawlf1Eb97zg|0iUq5%OpxYE7{W4-WYm9TZ)@n8s3x9_E(7@5b8 zRaod?JCi&p)y3KrIlE(kT?9t7z1R>cXO=71&I`LK&jO5Se}L-ty)2MiZ3I`E&Rwiv z==QxTS@Ws5g&2z^YF>@riuP8@o1{e%ZyF_TXs7G;y^xG`NaFH9i;YPBRsFu#eJI~s z6@Sg2?=PkzGWmyG>AM5t_g0SIpD)lrO%JOD&(VUtl*H?0f^ZD^Ah^<2P=f9$GdT$3&! zlP+PBB;usY`AJt5Nm=To9NXj@uE{r%$+t1dQgL!YesYl_xkQ~@W_#wY>zVt=Gv$~w zGVz&*`DY$0&Qz<<)Y_&zbxnDWOnHe(k&9F6^HW|aQX17M&9ksjZk)r8u=C zKlOtmwM(6d(&5oOQ4}H_PqZ54+CIjy&g-bnd5ubHCJ| zo2xlD-|qZExATj4onIPx9+q_8t>FB!`tu$dLwCF&>)+oUKlK;Dwx5&~C8Z=wX`Ke@ zHc+>Lx((EApl$a<61~;0)MQ(7B|79-n z$SwcqBKLxhA6rZGRuQ*A7!Hq@)EbR{^>6gF$sZ35Gp)a~_73Lmbjx3OI71F1ZWO&HyTOFKV2qx ztpsXv;aCS@s9JauB|L=_zLE%|)q+@a5lu{rQUpt)@^P`BO1ZEhET-F0ziOD8p#PT6GyyJdv#$_S6lKqqBH7Gy-%XT)kUaCVY7Hwkf&%jeuKU)Xi|QsiYx(&ftqm#@}e z&eB}YvAc4^?aIwvS8hjMktSU!D7aEof2BlorOfW?UAL?EcU>)yyedn&`mo^Yzk#|V{z~dD zlqNJtnS;_vh9NM+CAZ;{+i=Nkxa9u3UvmGQB5wnH8-~FC^$?grO#WAi$%SSn{~#u( z5YIXoZIb_M_}&DH<2~{J9ef8+e3N*@(a3v^l(_p3U0VEJ2$_fgYz|JgDKsatIYocT z4IxHCDWj41ZWx6o1$$rme)(Y;Wg66!11`BG#B`iK@LYKm6AhbAe~6%gV{V{JyXKTo4r4q=Fb+}@VsjGem6R$7qfy1cIWb;> zOKvz_E;nlsF~dg1Uv%Ibx($F_qZFoGJ*UZoe4cR|?3 zYB2jnGNGD5kQ`45+fvM_RkFWPaI9T95&oPhR1O#dE0J+#h`GHGZny&%;-wn`yG-H! zgyg-Ial@LqX6C$w3SJ_d2WGd+DZJ2ho?0v8d59BZAc?CKiMjB^5dTCKFs32-yJU&p zVtyKgAMC*Ifb)0S3U*NVyO07piq25-8z6!M3c(h*0L*U3%lM~(m|RRdDHi%cgkX02 z1YCH^Usx{_M79f}+l3S=38yfP%TFL8MF%h%*8vM#ffV17zdi$ zd_&}|xv<3m-$V7;T1~c*ea=|-oN<0RrcpVN1`f$U64_#|ds;_+xnJ!s+F<9114vqR3`S{k@_ncFqm zx)9GL!!>KafBATibnzb76Kj2PzkjyB#4+;;s-y0l=F#TOVM%_Q8{uu+UnZA_W%y3` zb;~^8x{Iw#wgygb@%2@mDLZm2htu)?Ai?%!(&q5&6LZ!c*!Z$OKoCBQvHb9%)rBRG zZePhh=GVUUSRwCy`OkTdn+^w5Xd0k%XBW9F>z+t%$o6F(6Lc;=c$^&hdh5xEytI#9 zG20p@n!=@bZK``n?n7^^gU}01SA3%R4YfdPq~$KvpO{fYZyl`E<*QfXxXDBBoP6>h z`c{7y6b`+gk5!Yc_L3MyB-U=pm&wesNC32qnlaO_Npm(nRl}HqDxD=e}ty`nqE}?c6D1peO*a-_M%xIPL+a!H<$!VUjFeo5S7!r z`q$nc?zT!Rd<`6GX>VhIs66`NU>2mzJ@!r9yrOrGHJ|SNto=+0h@Lg0MlAyq-068> z60E8JTb8@|8EjG8${NjGdTMV@O+o87oX84Y**s&g^I%O>NpfmysAPF_P}*RNU(7(U zPsyWzeULXJDXMZ-Y%M2ysAHaXU{i$0tF!ZlyDX-6zO)+oe0?PBBROTT(WkT`b6#xc zqBQGPPwaE;3mxGs03w!XiZU5)>h_E$;XhqcsTPpC`CCgYet>pJVU%y(&Q7@gV&Ee{iUEgh!e+ z6Gt6tUXb|xqZ8bKyixH75oBH2Tu#wB)X?Oz^`^1XB5W9-GT zapg`GbHd9w_U5|S`+*bp2Q^oN;)db46Ek(O_jM(Aem<|=i5I$pHz80Ld!Lc;mVpWG z$4Xv^QR0X`_9p5lxbr26!DWeBeeCV;#Q#;zzl7qS#_@afvA3U+@1HN=$mnRepj#h% zQ=CkXV}zAXf>TDqH%_|P`v6Lab`{;02v3v=o0USGm+&ZXsHsULS0crnLCY7R6e5;b zTnO1!3=@|j#DYq`C{ug^N#aV34P|pf?ETk}cl2LQ-XQj7+vjur&YBw;eWxC0w9LrJ z=oj#ePL4e}*2q{3DR`xw|Iq77F|4zyaKzt<5)@CN(&z~crazg*30uaZ^OK6zSL^Xz4v{K5;xtJjT;hNhOc z@6d1FE89B`w{>=P_iX$4skg6x<(C1?V3&Gm#Hd(p0nc%`i8uY$;8s%`isy#nxuJM& zD4rXN=l{XQbE|tsDDT{)y2q_c3)NM}r-q0sEf39rD5jjM&P^RCI^v2|XTmON7x9ws zw5_sts~(sZ;*mYgH({FQ38_CM{@RE4UaQAF6}`;8kg=)#fL8IHc;c;Om*xAHn!2Th zw2~#h*N$vkuXvuY?%H`|)luJrYVP%tk5>cd9QAXttK+x!Uc|iaT$8bjcBJcWir>l) z2flw&8G7~07(o^72)VxQhUx8$Im6b+0_++JUiQO0i>-qW+P#|dI4rEn|0MjpU;SZ^ zK9BlgWrSl?eutp{2W1Q@>X9FPgef`KR@J>~a4_<1>r#nDbsHgVVbegr$vyj%$Ab@| z8}4@X-(RxlbiDD@mN9u1<*wQfE7O*=>`Grc!?bLaz*+f^1H8W^=?J_=t$hp_T~FXv z1+}_SZu?;++*3ndJ@Hqb->SAPEIolszAS4;=m@+zEN$NOcdsP{@(MuU-P0!T(Vcr| z^<|qCK7TpC@Ku}lz?pq1AJH{Yjjx%fr9S7oa8}XH-C5w=+oLnu2VH*tJ76)7{Xp~t z1U|F(ezHbhiUw0*K@m6g1U}?2r`l3yF`v&`ru5B@c#;v)EM8yy0oe0jHthYFWwy9A zX2D?NF|Yz=72DvG5Zt$_(5l3zq!~YNsP*R*)h3^JpZd~=I-aGdt7}SXLrQ(*Um7*d zj_&=pe6*<{Gjj^N*Q)CF^?efV8*HoirClq$qt*Dm){S!4y!G6j;d@tWL<#B2z}~IE zw!Dw&u)&Q-ho`N;O+?|oEWtj^!A%xLf>G{nGyLF^7|a&@3|X)-1%EXIzhE}LvIjo{ z8aE3XS!)*l3Kus;68AG4^MmLhL}oD`j$a5PaD53!|1HIHL^MWkF<&Vq$BmwQ%j1bs z%Gc~W9Ytb5V-7e_FQI80A>+mRb8nH9dUo{O+lj_rOO}Y}1qh==rGAtf5ubyg>(0F? zl=CVx&?&w2`zZJ5xi_3tsWf{mWw4Y9PgJ0IPI-pb5%?_;Bc_befg(3TnO!O-a+JVj zRHekMQ^bPHnLRp-`BwrDLbEXGEQl9zNX4>LGsk$bt!+7nQEW7uJ=L6Opcm~AF)2_nUc2bzRSwueLM7}Uc zA;gMBfl$$j`J%@N5y@3R$rL408G&B3{mr6B5HSbC=OM-EwnpbA#)flmL-t*e{nusR z;eS1W+Z)*1aP)0pZv%T9*xSI~2KN5@u=n2eV_G-0xizS((K_~i9N=L;wc=2?_Vdrp zo0O~UBddbDa?jaRDc3crYkZkMubT*Fx+{>=nYx+o%>nSJ{JY;?zbW}S1?8Qrc^=sx zx}bE3y|u8Q_Tsu*R}Ww7IDMo3yX@*)$p@Er?hPtpy)&B_^6pRbHi_DRGo159n7qIx&Ah__~Xgs!G=N0f#v1bI)jsw8ZGOv`DFR1zy;yd(g`@~x^=i*P?5zV_Rrc2iBtPbWDM%T}1%GRdu zS@~sk-cT#E8k4Z{BsEjb~zU;J5sfGI#oQqFANd9ubXq3JCvZ97N{jCc1 z?2TPMc^n+DcXj{m!ry1Q^Rduc^_hYBkAZ4!aJU=S?OxyMtnPX;X{tT$rf(kzpu0`zc(W+!KW8M>hYRxK5v6;2*bB0e$)?kTR?m7JB+Mc0( zzm5z(FOBW5z^XqI3I|J!N;Rx`v7Za~eQ{hGVxuL#BKI|n^nJvZYCq1v7Hz!VEIfrB z3yoCgVNI3ct-_PaItybtj_ZpvgW`d*`t!Qbhs`)B6m9K@w1%Jk1Fu9 zrQtK+r*`I9d@qkXl!tdz#X8GRS)jw_q6vuu<_n~RMXJ*da!VML_|iAdU4~yS2{A&M zuaXiGW`wn(nDwwwPnqQ=sjh=wU58EZ-Lrg4{3>)j0vf#)5w=7Yx0gZy9rRKc5?(+g zIvD*i(~Tzoc|q(HXsges{LwrAJ^hAKxiBi7;=7{UxHr!f_j~U=9Yv0V|F$3|qFqK& zKj-Z}j2{1$y@7L0@^z-WNJP1+#O}Ho_m#b2^gKEJp;WgZRsul^(MDp4X@&miJ4%#L z4Hm@Yj0_ds56akq)3LW)!e|8Px0FeSFwfW=zVEN=o!_MBXS$UMU6RNY7fYSjJdl1s zuUdN~W#!ee?&`hfm{G5}D;44uJr$+%n%mh@j9oc1)EqW~Q%jC2$>GedWLukaf6C;} zs*JHgS~%MBdPUryk-SL?ZrcF&rUP#olDRmLw?fQT~MC+%qQGF!{!&=7Ky?`*O$V zDchQ~{rx3x9|yS(T=5&x*pwZAb=Cg;-~{8DqQbQ4V^63L?AZsCmO^G+z$I_+C+wD% z*m`HgeS#gZ!itK&i#vuTFexQe$g7WlhA zK42PUENhvv@QBM*w(Cx{!^6s?0!H&owjXZZ^IF7u$`M@({MeVvs~8^uc%&)EDKp6-yG!MRR5y!-Qgt--t|Kl!EE;rAJ@gHdTiPi+U^I!;X9TZ{bb_qt{|DRo$@Q5D%agx&6TjNd znFH7F0R*=KBi{SM^Zo3U8~2$u+0=med+&#I2M~YzsLSkU)Sa#>f9q1Ct|q+h=35mj z*J3fzj(hAw5F7hTDBd26?3=`N^J^UCCHgqgknDPH8p7 z{cU|5taM20o2Kn+@a+1S=Ke$TOp1E2^h;j^cC0A8W#HsHN6Qf*j%kAf2!6;HXB2@e zO$Wo>5td4vF$510q920zTOLtC#?L~9+d)n%%qGkf#qEp0e=m*wk#gES&*CQ-!LK52 z4kUISBzS9p#bP<(sZE><1@B4;o+h&NKohqI5LUutR#QS2P%PkR;sz;ER!5{n#8p|x zdjJQUENUktY?9Jqmz=O$9-lK1f8aLZf`idTz4u?#{FJEA3gp44Z+D2 z-!8J%kd0Zi-n*6r5Zp;ij)hQ1ZRffIx)W7XXJYSSmNGgm1~$e}G~iv+jd&v{5;WQ$ zLA+&6FM}rBh0t?F`VntYLIDKHgN(c4WOfhAcnV`gAQB#eX)zJwyp%2??gw>@B2=sN>D&YF*{&U7uH$!NLfQpbkIIGvSpW}S!F`jy;>GT z%npMy$12zp&8g!mqf6ix<3RykK^{@D?eaNvC`VqwEIq}UMP*xp5pN84hHLD%widv4 z-lHG!wi0tqrQ94a;%&>+jd+{07u({Os>g#7@28_9-fb$LJte+yMWVND!a8c=HY#I7 zAlL!7@DcN~p?rYg6$t)(3F&M)|4=1$cRL>yNcYbq?3axPwsS4i6d-t=RIo;tC^ZsB zW|9NEgt6wd@OI(>^YOrT9xm6#-w9BWFPdNMFJiY-Z9)8<&v-1Nt^P#3WGz0^PNIPL zTg((@8scxm>9^)TJpTUK5PuuuZ$tcTh`$Z-w;}%id&S=d_BOEh|1Eoy4_5D4iTg+P z9&y`n_=6(T_r-^+kl*6(j)QrNYlU-H-OOkK)$_te#f*sE6+7xymXh|lZ=UJC(u6W0?zfH?&bt%_qUo^!gZr}I* zW$Dn+y?xj-6f9!)Y+yn&fkeN9|WI5gm zH(3=u5fY}Z^RksgrYrGNog#eZ8~r4W`$Zl%R~0uON&t29#R$SuG+_Z0GB$^>Oiu7n z5mrEnt6;=G9e)$RZTElUJdfXtibo&*83rSb9)BN_5H?ClU)dW%+^mZKZE1`g2_DfC1M%`aVE0WW(O4<#;-YV)%ik`il;tL?ACGv!736%&-0PIahBvbQ`XdoyNXOn$WGp}?{?l9%peI!DY?skl2GfM$*K1jSpG$%SR|)@d=! zWk?gx{KTbj-fDH?Mk#l_EB~A*@z=6Mgfqe0oWDDexXp|2ub}L7H9;Z;&CtX>V!<&L zf4@SIPoc%21i`LGewl)B1>tx*@T|ojbT$cb7KUMj5n|oa7*a0W+9Nz-D~ih$5W&(I zhL+B!(Wv|cg=mjj#K{-?z^HCLpn7gt8mqtHZD8-el)X>>h4HtgA@4Ti-G;o|karvM zZbRPvx5~T!W;b5pXX)%}7+ z&cu6zyt}w{B`BA-S?0PyLFK$oaf7(-+S%`ltu}(Z`)GLR0;_9CuMFGgk|Q@I6e86cC*zn-$BJ-!tLn~h9?BM*uTOp3G=M{I3Z+_{cF+taI)g{gotnL z8}Fs`yAWTQM_Ji7wOq`5|M^uAw$rZp3x6P?Z?gqq=Cp?Mt)bgr?3qY@WcOz9N7w>Y zl{vk_?(Kq|D#6;WiL4B}cdk=m`*$!W@Xp)4$IQq5aqgY3t$MKO6Uw@|ZN;C?y?qLv z^0aT<->%f3dmpO~;T}Br_1wEJ-R1Q++d<_#BGyOn?fFg=IQPD#?OT^H{P93`!TrkX z{RIufUE@&h-#1qeNFMEJP?i-oo27*n&pX#msZcjLUY&RsF!vglqFIxCFiG<~9x}*9 ztuyDT*ZiveBCPoAw$w*+Ct}7oY1z?Ai{+Z9g(Y1X^VW1Ymqu>R(}H#H$PeZV2JhEt zZ+FRxd-6&KSN6{MRGOVNiBvOm(Pp1@MR(L^{BClq--xotYE7@&BW6)li>7&IWdE4! zG17>p_Gge%4c0^JnK<&Xja;E;ZqFj@hfmlFz}zDquROaB*M-#PV4Lf(kv-V`LhPOO zxR(LA1!UZ|3|w9X?p_oBbPeFaDjm^T`r988G)8DIYNRN^1&)k*qr+#DZa6NPT{?6QDJ#%L!9Q2}YNEaQh6fr9KEU@lvYs8Zn*1ZcZY%(yn zDk=LfW$wRp-MhiC?rliA4N12l={6+YhNS!Nm3068sQd4uIaj%j|F4?!h$84XNWN^n z_vb%p&iPnXa~MSms5^w@^Xe_ECSO|}!92~*1DbOkb*QGVa9y$=b5Yni5Kpee9d4?!uNZZNwe-*e;^){Wp$jst10lZyDI4)~dY` zqG$U*le353ug89R9Hi}C!-}4@A_)D2r|r80Bx~)lZ_nq>=+43pD-U`OHfHSZqez!` ziL(Y<)~7&Ad^~FLKy!X&`9QhvvTnXl%-H#9*3~}lz2|EN+I>CMbu-uW=as~Cl}=YT z`HVEX2MvA-Nl~>r77u7&%p2^nxI4UI9(JhM1FK1E)Z%g2ZsEj1N3609+ir%#IpE+K zSQBOFFd4_nz+ua9OWJT&vXF0_@Nw($vHtkQWW22mZ6?D%Y{SPG#T_%lelL&t5fxWL zjswB>XwnT65!|GIB;D>H_}=ngH0OUxx^;`*2vPhlX*_6_>owN07 zUCSIw42A0?YZ53`Ak(jVt7s_@s0wR-t#5^7p&5u&4{>~-`)q)ObVt}hKOe8pl5#Dp6ABPB|U5yTV32_)=I8{ib zl1|u~#8e8gwj#W%C|)f(h!$;Oi%#W>61;>=b8(U_J$(s1(N!S8hy%pBLvJLCVmS0R zXwLr{&AH*w+u%7LzjP`(=gq!&tDnpH=<9D0jF}scOu^iEzl}fdSbriWw{2sJCtWZ( z@@D(mi#}IoCq~}t^t`t3(b9pC+b7Cz1$C0nMCAoNEXGfr0oz@fKe!Rv2RKSlKn z_Z%OHY6geiSJR;HJL1C~t$oy4KbL$GjSMz#j`;auW2%{TgoviT^2F9GEJ4;$Yi-Ep^m<6Bob-v*Am z5%D{LU+o_dT%xO-FGG`1BGOSQ38f_X{C?;SCvJt2{&1}!NZI#DIs`{hmO~(6qX-Tm zM<~A{cp@bpM+sf;PthHEOSOcPay^1eC@h2?!9_Fy1bIYG%~H{_RMhh@IzaIC2-;;m zf=8jL`;Bym-UKK5ZQwbF(WElHYYi1&0x`V{4!xCB3OeB>nn4#OQ~}qTgjORr(IL1K z;XZ`{4!t)?7_X(w4-jgT%mg4fnE6&BSp81K4mndJW$jln{Tu*-GkVdiaa5*SraScZ zRkC(jgF|n+F_JwgkYSd|o+<_#-zK1JenuaB-$Lk?#y*NTh%$~7lAWvgn;+K)-xr)r7bti`Fy2mo-YNy@g#?t%+quiyc^mSXE0882_;#c4K=AFO;%>z7 zFUb>sH3DUGg05_yN!&^0AF`$Hay0?L_d7%)D4Pet1cz;f#WEURA_#Xi+6T(!w!~A; z!dPcgfU`+Npb(QUjM96~_drEk=ZlUaMWlAU=RBX5{({D=C^S+oOJva%q@uFkFAa4g|=0g=y6l()+3tE8X{B3mC)R`w=egT&A+*coCeU{0` zbbq2}-|1nyENhwKgOM!qcspo`nKG*eQ{+?fIb9evH@lj7K0@0YI1;V&P5bglSn!Mm z!tYyIkuS3w`?5*Jc1LOkFV;2or&kPL+kax9(LSxU)Xd|3{K&DE+&8LP7UnZQ4c!{h zpuWy3=}#$%=rC^5yeZdq<@soDM>GvyHNzIr_r-qnnK^vg*Rx2wxk;-mov95HV(;Iw z7HbMehE%>7O$LU#2CEH-9OXLy9Q>X!)HOz7nIP-=_OqIn# zVNvE%{I&|5m6@qAJMLecc|!&RP0x=+!O_eDvB7OI$?!#qIV!=O<>|SH3jZv0?2y-jOUZjUkfa2 zYJMb|7EJ+)wYXiCz;aI9Yc4qMMc(0R0?6ALEeJ#k-KByPVxgZ(u+2eu+DjM?gzuR` zVqkoJt@=Y}(H z!>YF-?KUv?#*nI(#)Oc3x9)_KH6Ga(p|xETH-{Fjo4GmY>4v69q0clP6T(W3HWg1z z**Opw_VW3MM@u~YuqQUk53GL|rs!ug`hPx}{dz;g$&9fPuQDEQb~(MR=_BWLB;jsE zlkEKj*gC9LiPsy_B1g1=xyKop+rZra!ORWLy#Ip>=k^xk;L^J`)jcHlp*Pkn>3v7< zPgKCs&>CsEOZ6va)X-Z8D~zmeKkiS3^Tak)v#&%~I0viV(Qj;QzNXzFXWq@xrQNZg zh48u zmZ-i>jrmxyXZq`=p#heMwo}+NTotPw5jx3pWw+^Yb8Z~0egx1PE8NKR&dA$(gjtKQOh0@Rcy*HTke zR1YYb1DG2@W5TFnz}#TfTS8?Y{y812dOOk2p=dg-xr8W@>(pu`N^&y9G#8d|A5FO> zqRT`Hm!$MkpjJ~QAfX9+wj?}9FwV*-4@Hc673DF6(Wqivb7JiF*DZ@JK_@hTWib`; zHAJU1Pjq7X`7?U~c1xKm5o_(}syCeUSz^)$V?Rf;G-!6Gj5Q)aE+ z5F|$-VNVcqEY)m;16UTLP7VZGb2>DWGuxas4PyeBn+oNAujXC=W^-3wqlmk+j0;nf z=9=@CVQ2?d@K&fWX5vog_=W9>yTr6D zt|oxFT@id=SHWfx-wz|;%lY#mfVl|=yacDH#3NKesCh!RhyJ0+aZ(jMTPoTdjFE#Un$qAf|1xNb@3oU0>pk2^}%F z?Iz?@-%35aeE&}wZYg;lge>y%_T6KXvV|`XwdFSw zqfQ+5+}%*GU>*}3n;<`mO_sk|_fd)+etuZ^=2eyVz=^u$AH$RF8}kJbVYi}=hcB~h zQZEg^aZTP`r?qd+e(A!-Z$I_9AlVXPg-X~pBi!3dW32 z#*Zw^`);A;rO;!7gtfeJ$IOq^o3Xvw>j@tsvcv0-CC|#UiRr2-8*B-BJdJEW_-(6VV?w5no-w!ib$kVVEC zjrPvIh^qyq!#!1HXaU zT%sfJKbg%(%jRDr?=^tHPyJKb{5P}tK9GPG!Dw`>+HiuBFoph^W*>J`loUUx{B1+Je&7tv4PP)jO4`=Si0kb)Cv~0d!A9))El6s{k z0|@p@5er1#AC;^zU{$O#-Z<05)Rl9K!XA&|G{|&j^NF?`>mNkKjzuiG_Qq0^CCnv-| z3D;4SpnRe63PFfkxDFwVa1{l}1l!7lQSCyUxkxwc?Mx!sno!gNTD~YmA!3QeIgnj9 zVB-Bqu^><&$`oIq#-EWG8}Qq3@BP=@dq@9U@Y_D0>t`Tv1A!X|+(6(40yhx&--f^^ z+BV;oR$V(8q1*Ki&vQ9+VrhKgX1umvD${PxI!P!*x=gZJCY(SF&e*u8xiL2%eLrgk z!|!#Mw=iG){zJ&y_FYcsfg+hn_7B@L_e@%6WBq^Fd#|u2_dabKr3Iw9ECf)jOTdCy zNf5$dB-v72#a>#k)KF3QUcTl8S@*=r<=V;*x_q1CHqo@Wddd##7c zsu8o!_+Pb8Uex}f=FO9HPJXs6xE&%Lq~ct1$;gYrKlmlbT$Zy|Mb^#hLq703*vG5A zvDwbG@0l~HFm2eceT^CN(fW6a!jSeZ?`7Cf$epPtH%1m#+-F(UF*RJ>7 z)%+!Por&&n{Y`+$UG+?9iH^Z>z3Zu! zI&Q*Z6WVd2@vM(g3~8~IxfjKDL}b!{E`q0fnZym}d|n$@?N}EOO8LGje_$n&=^88chH2T9^C3P5c|BP0%*W7pgFUX41m!CD$`V2Z=@sFI&j_doR9w%(&eu5`hsuUWd(IU_yt&xY zL+Xyw#kt=9XzZ%?z}TFM8SDITwL<#T!@QdoCR_ZLrdf1U(U&=+0=LqxCG~RR5`O=8+Gtmpp|tVn$Y8Fc@Eyi=q-@yy+lT1URN~Q*uU;)# z%sETi>l>D}dyI8eAVw;7H;n6^evdOHg0-VO>@ z#Gzr-(MKagN5@jPH1w1AM-4k-ziZqo#7#j*>7#=qV{l^& zu+t=wGf2?~!*&D~#I!ZUxDm0?Z0vmBsD%QrhDVy}KVtv7E%uAReUaq zB=G&nEEpLaz)?_=IaGr<5jwY*5@_S0a|(pa0me0O07sJr^Ew*LCxMoTCA=K=HGF~C+QY528Kp*8XbjkQJ zf#zfx<0gUu2)u#B0NY}^Vg^c$IZI6b!-+Z9kD?^-4w@-h6BySBQYIkqOXCFIjAprI zvet?TOFLM0sD$HSTTDz}?WYNhYiE6_N&;UlP67np1x-G%F?n}!9GslIUk7gk2pp5( z;-m?TYql^pAn={z1kMt&_qDN4p=jH!IA?W;CjfzWP;xCvz__Na#sLJr8Oj9&?nUD4 z#c{)OxRKxhuER^{z{i?s#usx47+wIMM?vv#K-}}Byu&7ZmK7)2kDuv;=LuB1-l}b} zztwXd`u7vKnTowt?5$#N6??1LTgBcg_ExdCioO5u*xSxrqhj!G$uIVH8B%VHDR;ex z>O$v-e(S;+-fsiD-g(*UC6u<3k3h9n2{y*g53M^m#Z~syXt{x+ zw`6QII6z^lc;!0wG=I*whq!XZQ0vgo`Z9$Y39}ZDPN=|YNuqydYfTd3e8R9Z9QCxJ zF%4XtUj=R^EYv`B&d4%m7B6x-Ny``-dz2gVr<>kvbnN}17@xVZONAi|p>RuRTwZpp z_lwx2(pZNo&6UEqbjP@po^kU@agJPVdt^MlJ??~B{5;F}uRgllrSYE~w>v{Mcc5c- z3G{aJ@ZrRGuif~WXnaj3{MwrASEUG31i$YI=pgZH= z*c(P87b8372@J>qR;jCa&Lry`hvz zKgjuBrwG6vWf>mo9ypqMjj67+L4jf$BAmP~28uCWPgK89?< z*PMl6HA$F;kSPnqENfIELo>k=p7d0}GOcDU=O@iav5*_V^_#L56?-!?X{#U8UV+hc z;3sE6lRfq&yYLx~ewsVr>}(!Ahos}mXZJ$c2R5>gph&y;aW0wc6L8ugC(WaLP7IW@ zg{O5=#(9h8?91c?qZkKeiC5IP;U?7cWMYVnt6ssWveW^)-VeaWm>Lgj#VhjU(M%{L z9nAz3H_>kj9mCIs@?CBC!6^P696z&$T&ZGj)vov7yD_F>?+~U+wx*J;sbp*A&(Azx ziB%Q9HALkN;ux&Ornv_o0@%=7di07tZHR>s_}HG9*f|f{TA;q^Sov+Lxy9xcN=1gz=DpAbx6wk>eCeAJt3gLAM?)6C9%H0z_yf^E-d z=?9(P`f$z7`A(|h_qG2u?EN}=)gA8trlm1e^8EiMc^;?sm*lx}X$)x2mC19i%j2^q zqu<8w-gQA&TJIZrfPL=CX}8s!h_h3G=G<^Y8-1zNvP&Vw_e9+$&Kvol+w}8 zXe=m%%Vil46l#(nscnF~3aceWD}0pfZD)&P2H}iEXdR@Mo)Bjs3_NHQV~UP=uN^)E z0*c?~e2ZchibAI$wB|r!KXStayJM|kk*;53tc20?kdaq@#KL$X7BH<JF;<1XvMj`dGB=Lom$`K{JgV8|o`!>Y=ZZ)-2NIM~+wn1pGd}%*n z^fx4`XN9u(eON;4flzx9%B8UaU;5!px)xta<7HC%WRN`fBWcuVYQveGfX0gxK=Y*>;Z#?Q=3X(o1i?O#liGx;ftWn8-# zDS5J#0+ZBR@YLI=)Vr8eAwTtAaq4|>YMCsx!X)h>JnbBL0QO;tS2<3oWt>Z6@g-;pv}I>0dGF5`KDTae9|Hy<3*9_+gUq z6Q0qJ${56CNckD^;*1e-hC-I1W|}$KE>pujQ!6|Zl9H)=FH^5E(?FhSWP0%ryNlD^ zFU}0V2u-(EZY)@Jp5{mlofE)Gnmsh)A zUK@THmU7wd-evp7%NyjEH=17AY7YPF7P_K<+gwY@Rw@FNJ-pEgAE)q@D0f_q~?q z|E(-=X?^gSR&>kk$eyu^%s|9aQ+oB4^ELi<87~CEJ96FgMK?m9QCHq;D?snEPOX>R z4zD%9HvJGry5u+YNbiST>=_=z#l*(Ndk5f2lNBTojZ0kh;PMj&%HRW)Np5n z{Pn5O;1!0sD5q?bHv;e5f-0v@`6ZLtIkb>VZnc$eV?p&Lj5~RwyS{C@+ej5wKFK34 zYd@0x`rSdj!DXace_ONRoa*hDZ=HGBb&6>Enx20C3f9^1wA-+FIPvc6C!_iSwr0(^ zH6ac)-}Qr5nZ4(I2_=f(>xWG1I|<8 z=FDok{55#r+rtLLXQm&9|AeiO*6L8IOh2tSDCKPHHlSyleumG49XmkNPr7XS#bYjR z!oW82^&i&rzO<{RZgFY^m;Os;qmp0NbLHNcvI0&7^er112k|18pv+aXjQ0Hwps(*Z zP_?*03F1EQzb(~%G7jQRhr9gB3uiVfLHvSRU^aS5w-Ur#x-rB@t;Y4}8`|lgZvz7x z0P$>#GJR_O{jUKEGa&rHLpFU#u*wW-m47aH!`Y^|5STcD^8uYcI#Q!jnv%7fX>4NS!3V8SOT9 zke#Y1lSXxqY~Ip3ED2aIeQg6O;P*#Tz{Izr-9g^TZ5fs3!$$&6>&b_)Kfe*RWVX1K zMObDZ7J@`iLThQja9WTPZVcQX$gnBCTKYV!p|5weP0TFc&>2WAV-nT`8j<%RW>Iy7 zt9R5qSo8vco`p1a&rr;Xg4pj3u{COOFj4SInAYm**hb^93`X2$Qs`iI`4SZ%jnGB2t2Qvrsv$rL>+4@o&-8 z2GIWo1~y@0Jw)phID#YaUwmm*B-%GLU5ZZlF4F83(ZS?*Kaw)YqidkR9h`osjjn4# z189B;#?Ufh{voE^Adx`;{jrp6fT4+Dz`(`~ATc~}%!N7>{bJ@_5p!-aYnm0yeK*Ud zhHOD*B|w-iNDYmzTYKpvefv}7OL&GsbY9mwp&T|R-fDy;vitYyFycKb)_2J(A4}h zePv;qW?Ycz6|TD&*7D1%$|Hk zcKq;Bn6MmTeg&7h!!z!Xu*}3IJGK{g+z`tvofmzI?1uVqBn|q+Xl)*Jk-ffY&&Ma< zoO?I8rR_4;)~NdQc7^A<=f|}o)T=+vSm|wh12un|Mor^^^V%MVycV}YpNiN&gQjPeUtjTP^xk{?1#S@u;J{Poe%s0LtGp zmURxTgqIPwS6%aQKE1;u^v#0Y6Bpm^`F3R3?qzOePsYG-XZVO*RA>;2Bd%{d{#sr9 zV*8z2)9#;Mc6}QXk!?NUBo5R-NdnMg`^X z8BE-MqY!ZNW?ZynxbJ2d<`#I%%A z=IF%Cb7EOzn4k-81y9Novdl!v0J@q;vu*)fG!s%KbGi#0D2{wvlj%= zI|#>WC}(xJ;V8;^6U`7OZWv}t1e}*8;+~Om{Y-dwH}Vp#5(qL4pg#A9Q?I_@aZJc1 z0`-()ewvt{E(7eHatWSt1(l+*pI_Z;r_!(eJ^D4Dzu^cTk*T9PbO&C1M7(XfISuhL z`Edet)$w}*#m&o(+h;i>%+j%3Sfhe%&)BkM)tP4vHu!qP|M7JDF?`-((#2DzXMNp? z<_TKIN$FI?CGSP)zNm!7J1UP;Xr++L$5+<*BB^V230}15@70%&Y*6^RF*ZNlc7l0! zHu;MC4r{-INiGSRCzFpjJ6%0^@QB|5jtaI_uq|KuA%mqb6Vp|&t%B`0u61uAXH~GR zg6;obz_$IpEkv(6lXmd!_LCbh*u2I34r}QCGM;Nx+q8_v`;K96cJ3MNE>y_hZ!ddX*rVH%>d@b2Yb)DU z7PU8RY?R|VR-$O2Z*Mi8tdJhPKQhS$yY7-=y381dEy6-9q82w`b1ZPWmSGx@b03R5 z$9#0Bz+!}s+wWO;e=l;nsjWR-6yw!^gBr(h3S-W+#VnGB%!Qmc;q4rBgn&bLNPA?e zPvnjvTnRXIqeHAfj{HU32H-k}#{bjQ7$W}SMz#Mq?~Q~)CcVHPk>Wj}e>rsX2os6( zlK`7<#s4~V!-&oT{BP4*{2>Y9SKAxFHiVcTM4V`Q?}F%J$4zUIP!j1MiE}Rj$ylVp z7*Cw<;1M|>aV|*UP0V{kDH#ydx$&v7yQBo=ytj~;YpI;~euxC;aV;T$Z4v3-_`LUC zDMfkchEb0p6FnJ;A|5r{l3XXEwm_(Fz@gic@=m1L2-@Dz_-DYhCQO)^_vRyMhZwX$ zaO)P)ss0%O$kW-SvSR}bg+&bP!>w43b>|FJUdk}xqL_9gInf_ zfw{WN6Hh-f=+Qm*xXfkOxq_KTcllSE&UtpO>S?sz`NH{hy)!k7TvhC?VsB3r3%&vT ze@#}g_y1e={@>L0R)x&}vyeH)=AT05wNG~S9)z^DZ&c2F|ND@6`_}1#I_06eA+3F3 z_LJAY*?W2To7(3Z*HcHk)|Fd4+cb5;wAL;c^(1QFZW`@Ea)rx3DnsV8yMw|Wzt?sh zxPRIMid9<9^ZP4oV}CHJia%KF(yIjQofu}nV17L&#dX6|HrN?+91E>o4%*&h_8iJ4 zU|KuU?U*$@dv`15t#QggJ_wo9&xUV%k=}Q=VRd(y>&VBU4O7eMkv{@XCw^Rq9jbEN z`g30SNcH+Lc^*A_UY4u0Q(dMjGAx(eafNgnwhW)>iIX;D$V5|GMnWCSyA+2XDCA=o zM&fYTk+&^D2SsCJ=ZM(YOsua3_Nf{UA_|=hg=mn%Mz|q2LC9PZG6e$B_YKwJ=}i;H zxVpt)17i+yV`{5F+Z)|Igr18;8w$coLEF1J!a|6#f`!b7MJ`9jZETO(W20s-`WNiI z1*)s@BL3gAHyR&fq;^R3pRhNA0K()ueuvD@R|Bs5WmCv_7)Ox=-+K`O9g=p!|wPo-uC9jqmg6~GS5MiFMypfU|N%q8JfhaNXlI#C0n4h zoQGE@+#ENp#Y@S@0+omEL>>V0#OF|anWb`P>=lIY7^0=Locks)MM}$gJxpm@yNRS8 zW+->YHVTwGW7nkAk04}DB7d&dlpyKt0$LZ3o+P1xkhwxg=o4xV!Wa!C<;*t+0vVtA zCQQmv&U_b*&wRU&&wQIYQ40m6KV;0y2HO$Oy6j}!xZ^C~DH$ipve)5Rk6SX1g`(Tepeg089#h9XQR6Fzh zWCPzrFgy7_?zT?4s%6{jaz_*Gmg}rl zI&CaM^MSVENbmIN^i4k>In8;hc%=1+ZMcgeptBrF#OCm*TMy_kCj>Ey!7pC zxCC~;okLPQoLdm>``)EihGe_x@87-Lt>Nm|HGBMKe{^$`rd4>~DPVSf@rpBfm*O0n ze`eXX<2&Cq`fx($Q`et7v8AN!+1=~;r#(9MoGwZFQn{qaV%JHZDtQysdeMDb`yK;g zQ@$IR0ROh$%bytje$UykqKxm|QG1&Vv=G9F#&tgk>qb5p0mRbX*qh)n@)24uthBA` zrG?o~pA0Fkc69m4N*(!Z>012Mt?nnMWaNvDA$+Q)X>Ur?$XC4#p`t3Rt}lIf)~C7j z;@{-GC_^Sp!u4gXJN&UzTC9wiCuq;^7+?4{54t+%zJaS@$?K3K1GiG6KDo8u1D>_e z8xilP9;cVo)Rl6$sX64&|b#x8qoIWx&4ST}d9 z{dTf`_~mQ!sdo+SyMyZG?}rpp@>tZoSVQTDX=8nrj%EFQd6AoKj&|2!%RXOr80`)! zmB$IAn+x-!9tMGWx1*g@b6)h#r1Ft4?QNjm-8wj+eQER<)?uVJI$AzF_GM5TA{`2r z7hw-HV8^()#o!2TgYz;D(GrD21i0zQ^Li3(15vOhG-j`D%nWJFUJZi6VyaWKTm=3g&-?*tUS8-Eb} z&kNsDC54|r61>rbU;APr!oNk#s|lThc6)!5cQ^un>^FHIgb_l<>)jJq?=Z`FL>m$8 zi-CywK8P-1!m}1fBGG?G%uhkd$8h9pAYu;1<0NF@IKK%=$O7aIBIf7>#>pkQB1)Nr zl5a@?p0!v6rFcAIo=8gY{6PT`b2c>bG4QPM@Rb7P)w_{IcrMZep0zefLJE=!JZlX? zs)R=?7EuowDIMnsOX?RG{T7(_1`%^;LYGkUhlmai-~CWVnlD|31Z!f%VS%PX$Y_Ny zz`j@p1Ohy3)2)a)d`&$*vs}V35;KomGG_qCc@4FML^Q>)u2(b7i&^dn#sV2jhGrsh ztkoFu;u;nVg}2s8vM1A4Vl>y7BuAr@;5Dpu3ULx5C(*%AbF(-Z3!`l_VY{J|oltC{ zF!?}Rvbzp`s}XRi9g)Io~*+;F6JZS*Px0 z`8Q^rm1hN-W}mmq4tCED4bMiWWQX6&j%>`1mS^Kk1+jL5cy|FITtG?zFO39=jRKln zz%b2W+2tg==YZdem!il?xtEjHn3FEg$uzxo$?n<}_iI_<*90louHCzKqw!jv{91wO z^;>q=Z@XW=8-87wa{b=D>-QV4m&vbJnBI73cjK}9jmq#F)hRcg-n;R<@kX8e#tYNj zS9ZB?+;iWB=ZaEt8}H>dH|Dm;bK6YwKHBAdcF+47o+nAk>%5oO)tJ{U&--DT|I;r2 zzsIwN``gKz_7~3@R7K$`3h!1?xSc}RpZ?Hb%88qm1`4wOt?ky&{8@Dyo}IY;YJ<}m z6@~9nQTWme>d)LNHL|8I4OmzYy%@NC`Q39dVK+1knL`gDj~Vn;hrEWvp$}k-uF&uz zuRb{>#=&|eMDCxaE59brioz+@5+*YO1}Yl3g|P2$uhcoQ2NZfV`kUN&lU+Q+o%-n&7VL#a`Kmud5|=OH1r zo@Wu4JsMAL7}(G_DhacQDDLD8E|f(1Mm+PNjL`&_+G~3@4-bcb`npyyrLH|LV`b~m zpyng9v;OD3&`l4!2OqCFd=9TZ^W&tVs!F(`Yg5LWkM5ak7wJ|^P&mDKT(Z^xFul51 zQ8!NEBmZ>&u7^+kp#R-PG%_w(^P=@#`DHsV>h8OG`B15I`{OstmG7eaorflh-d7^l zf8MwTZv7nC&Rb_qAE)rT!J@ePb3?(>7>X8JW~TT-GQ8IsclcDao}xE+dE`eRSu0y^ z{)yVE{n^$bw8jmre7DNKOiS;L2vqd%9*UH>)ypr@Ti#SzYj^s%2E8o;oAj-+Z`yw6 z!a>p7rYNeL2q-)|sO^?^+0Q9+BR{Sy|5>RW+*NZk@{8@5Oym&RKSYk~bQ>FLs4tWD zl|@y!U}v>zJG4~QM?dDyDJ-Qs$`yWd6oX9;-+JccH3zI16E}>3rLka{#gX^K=+Ut7 zF=L!AELa_Ov{&0un}qvTgqw~I(H9;)R-`kv8l!`ZF&6|GL%j^824+IcY-x-+G!}jk z_i=YDBP`Yu5wqCRcd1n0T8ajX-@33kXB)MDkv$j2Zv*iCBmUn@)_6+z{?D@KXML3? z@Le!s@OaTXSQ78?d*yq#2p=%9@(r@*JkmL-9a==90m+&uK2wdPq;L^2mPh(+J4X;G zkT6FhGO(Rz3FE`jN(#TuBWH{+jopGIF#aTFD=D0kQ=M>MM2VCnOxVr|(1c?Q3MhKh zgp@K-VjT=$fl#h|zp*4#LbRSLDLf%bn%E+xzCVZmNM}RneNwubm@pvJ1S{WS0eytW;6WghfbG17pq`@%R=$f#3|$QK zP&LD_m^sUjdZ(H+-Gp`3mkC9&_Dkqs<+~5cM9pPc>yQ`vu~y3BEpu3Ft!PWhn#+rm z(9on+eylZ^q|Hu=aEvBc8cW8As9@!L4>Wm;Ecq5P`5-QNe@^^{nq=J$rDQGLl6?S3 z*8Jk#$m|gT8%Wj?q|8gc8pp((Z-V3#vLtUZC)kN}#7XlkpSuCZIVYwCp}065<|#~K zc!yH5)*@9()_l<1eL%9N!zH0u2|%*e!OT2KVd!v(aDFY4&ou$?onW_HRrEGh6}=nt z6@O3BJM6!M!v9dgw+g;h@SU%M?>g9@GzWwCA6bee(=lJ#(x`A-Za^NQJ$=FCaEV9- z-$zyOO)fbS-p`(Vc(UWCn|sl>gD+Ygc(`-;++Oax=~<=+9_{*YeqT!L{@@h@HLedt z`_c^#P??dR7uM)(4PLt{yUXgDU8voGob5BNEx)?x7IEf@^C>U^QzX3XoY z&?We~SM9eWkHX)%(UxpK@9b~e)7Vh$5_%=(`xjl;E20+%`);zGzn|*ii=T}A+;XY* zyPwB!Q(NUA?9O+ivyM$oZ7xN(p43*Z=zG-sVtdiO_S(~-hOFi(F=02qIQK-;8(TVp zhc@&(`$rECw-u?p_DyvJ#4F=v{aKFpB_B15*JtHPKnJ`HnX$Zf+QX2;%SXT284gG9c~71!{HK}k zFhjiBpfY?u+Vk78ri5=0J|A+~x_OS;>(q*pam!l$;T{SwoGUGB5#~RrMg8Xw&cI>Q zB{(fu%&h9j8PapcmIfvWoS7(Qktkvg@4T0>;Q|=$Pjsw}DBKDbG|AU+840@_8fRCH zS>+q-ZV_u+9Ss-8Z55!`^FroH^*33@PesPN_(nUfjd1Ue+aZi^vyFEJ{i0}4K%eG z6u)`2yQ0L0Zijbl1T)|C1YcSwnE6K2dDXNR#wR=H(iPwYPEyW%Hw);^#!($V=(=#) zBqxS}j8W;!kTgVnDPYWmQ}uP2p!j{+lG$%eYz}13H%T<9VJ^b3_!6cXg8A8oW!;hR zCz-Vp!&+R7o6FY+mNh0IZ*h_lDrqJ>Ge&=#ESU`M-*9%P zFnO^A->L@E=A^w&>?6h0gHCZv;RYT#?87p)Z%*Q|jyNZw-bo#{4rgLSSdC9EEBa_f%>&#)d@WODpGX<*YGINiYS1j7U-4~=rt7>3>O%g-TY(S z&FLODXGYwFrrtCyxjC!prup#Axn{TKue-I-R)RyS*9v-vQshrp6*Js_bhj2v}gZLb#)I=WZYO%JlWCcCKGf+N*?c+M29=>%Hv?<)K9e@i)Q%gnP)} z+*!0b=~IO7F{xZsy?t$gY?g~d#*k?Z?e6WPrCmb?5f7dyp4={~{eJXyidbn#C%^oD zKDFfKTcsgA;*59N@cXYWVYr6%ZK2E6Tcj;_?@zNoyYQ>2k<0*nYbf_vc#x>&sgdwu zgmd83p^^;o;FE_I=HCd{r60!jud%eO>)q=SSp_Y+RO#@tmz64SdbbvxG&Nl>spS5v zIdfJ%dq3;vk^3WGnKYS#b-<-7rEhZkMn~JHZ%6$@`|g`;at*%}vf&GJc=gA%Zs9NZ zWhVtqFFQ*Z@;Af{J(n7WCEf4r>T>G7^7AjN{gdiEqIt_a+`V6_^YOj!e?a)XX?tEv zr9&2RK*WpA#LOdU*C%xX^{+cNH{eSZl-UfA>{m2q`@=^b_P0TE;_=* zGRV`|aJC3L2O7H=9lk&qH0)z&VTrX8#;*3oEE5GI+x1sKv8#}A8xUw)WJrE?+&X0R zdTAU&8ny`;`X|J2n{WISUi@AJW~c9Im!T=Hf+$BA{*Z6j0ZV^psR4=?f8w4mUv%F?=2#Ie^DgJq$8k2*=l0VMz!d_?0ru_I7w-78^3@<$rCeUzf|WF zIdnG3jijv+wp}OT_skfsnv_1#00(>DFiK`M=CDmdQl_#2ejlB112zufVDB47z9R}h zFCe^lL3s`?;0U#HHvltZwIs$A z6mw<`S-%5l-}V0xGfNOmV~|6)BAeu3hmZ#5e3k&hS|Dc4N3k@8Oi?Clbq;B12Mbor zu+fQGiP2vpOCo>}Iy`w)m}IIDCB0Ki-eE=B?3C<+qHTAIU2bWx6UB}eB?Hy@TPWKa z&HC)gK0s!>!#R(u*|2J2r5b0a73YMQbDKoH3!4rX=$*<@hS1R+3Eo!mPepnmI>}*t zZfr3*q9%TyOdl&tih+aRIT`PV4-yl}PDvCokK;rSBqv6QxX*3)$vO~l0nbM#sxIJH zcB?MnBdQCyYU5kAGp16XtMqHC61XaU{y*meu7YnBeE*liw^<`ll;c1Nd_uoQFKKQE z@$(7zZX^r*B1hJlgk{CGXuE=qZ*T!8u4~<+PcENU+3Sy(*cqD^-TQ!M-MCIiwgw5} z=V0S|g<-cap{eVpQhi=8zn8S(%Mk~d=-f-7J}(={w+PSJM;_SM4B>`WYsz_=-=B z9f=U2KF7*F_#AnjN?h>M_`yi02e!MoN8U{}k2rd0Y`Cqpq;Ds^@pZjo<_A~oKw*8r zo8bs~!tU}BMO?iiHmtn88~bErC}3E=&VJ0KAtI{+t04?mgq>5fJU0(YzJh2i%j|E&b> zfz;FZ@gLOZJOVgm|62+CFFRwAB4V^J5q%YY=vVw4e?&<5bpeME&Pd1gYvUzw9+dRB zU7aXWkkh5)eLNCX=Q>7xCsB)bA00)p(c=w?~Fkc^C75M zux1&Os_;~PT8*(mjEAJN3O5^WKhc0oV|`n6}$ zMCHaeg!)CK4fJdAjRVi5Q7ppoV7xhw8Mn9#!q{z33EFpc_u#@!1qsR@=dgss}p076&o^M0xuIK zhZL~Y#q1+FbWbaezYYWGNA*V0efgX&A?FN+`$o$73gt>*++Z;^l+2CoV1zr-BQdmS z8Mg__ixcxO2u^SykHk+(kg*awc)S|sA77a)Od=V@|1L`6!&6ugid|refS;nefL}eR zx`0n!SMdM#0v`U~bOE2PN}#Kb->T!c>iDfXejBNd-{x;J6vy{ln$1dC8~-lu(ynBQ zsm;@wl+fvy$Zg&W{%0M(|M%(7A3<@y>)(O2!nF2@1o}CH6%5SW&&T^whG+xD?M!WF z$>u5U^`NGmLA>c}51rWg?n@ibns4!5xoBeNyN+}>c*CPe*VR)#y%xBHT-y@$Md&Si z_G8ie5wAKf=LS^Q$d-CJUb z|B_SkKVH_Y&Cgzs@CJ@X4%qoVQ2Rajpc%C-@3FAqd&hZ)f#%ToA#1Sn?Nkzx@w0nj z*6{A0Xft^W?$1Go*Umavvym6W zGffPOQ*AdKip5@j8D>=c^zE+mz0u9*hqd~jZfp4|3yrK?#C@!I?q}WWX?s6s^3apI zEk}YJpluR_>%CXpy8f(nQ60w(?3QS39Qc>hVGwKVBEx%X2OHWM!@QGy?GV z*u5`iOxY{jsJ>~7wkzx3FX-u0v*=N;_RKjsUjMcj`DQ$CxaGC^vnSQ1KZscA0*e-d zQtoKa;n|@6JtjNUS~{OKXZXwV`?44HWrJ5UAf2f#@}f0_!79V3?gbBruWDmISJx{( zq?Q{=>hH@HL;m=|rm?{ui>)Io9LDZ{`=C&hgja@PhKtanVUf8;I9&urW((2u#bMh+ z?-bys`=a$NA%>zbZGqklQA})DObjE2Y8*pvm;!dbgS2CDb7LclVrp=)Hi*#0QthR} zP;1NR6}-4hwzw;9aVd_mX+u-&VDT@j;tJa1e8b{Ij`6#YA*)1Mnnw74;%@=|$;N+C z|MpcLv;QJt`)B?RA-Sa#;$z48`=AJa>URSDXf+}FFUN131Z(d}N>lQ;C7vXi(4UhS zXr=yK$=@&{pM*R4gA9(}hY)&KLH(OY(FjaXO4xw@T#~?;LAs4htVF6^6i}WD6aA_w z52Q-{d9pMC8<_acl2TKh*eImZA&^%sbN6* z%!d&7$55s@Tq$8YiDsIjSf(8ebQ^QA4r{)c<%wV|GD*@Uv%)f2bNndQ{GikvyQTT$I7o5HGeaj--qVQQ~{O>f8JHv-c+SuJ8N$yXt?$|r~gL8g(!%kdp z`pUwzkh9B0J6q-38%m$p_rO0aF&LH)l~+9Jfem=LC^~0WKot2nu@GhZlxwU*3ydZj;U$;N!8*vbAlH`tptd(T(Dev^7ko}xFs zPyE&FUGdDAnf-PS>*5_8AH8HFf+^a|o@^=Xufs!eS+=zuE)f$2aF9LUn<{^Eac10^ zojca;jGGLy=K#N*zag)uysWF}-xR!_p-xTF_aHq&RCEMZ4g=Hc6 z%TZqrhsfttZwM*ehxr;BKWr_s4k;RYpoUgD&?gGuk+!#+UFYG(&)>@RvHJ1sIYWux zuCj;I_GIFe1@Q8)2b9?}*dTlE=qi7e8r{oXes>xS6u`C98&UaBI(%G*!SZ*1PCaJM zq59!>5gP_?3juz+MmDv#Of7XS`-(LTJu-bv_JY3QGrbk^#iO<7xvkvhO8sc}&J1~? zuuM9TK6-ZAqhX0_d2fzDz9?~Q^oxaDrg-Z*CMTMYP|#!2PY!#%!1|b)1TD>m$StBr zjNKH#z9z&?w!x8i;~K(n0ya($88gt1BdNu_vxq6$7Bf>C(?X2FXU06OieajQ^LNyI z1Y{vEa#3}#2RMI2qL#oQ%R~{&MbXhVarUstHI@+D>Ik?X;z2{)c0_~&6tYPYzD1zt zD2>nJ#^Va&%d{hEi>B=6;e}PvTY1|1tHYgndY}NFWPuMG!lxP&%n<~CQurZKq^~gY zpdjku3qq)baN-RfZA3gz+Ip%%D-22^`Vh|SR{M7aaJtY5E7F{_Tj@amR|Rln!Ue$Y z>f;V{Q3A!1>`TxNQ4Ln1RXY5kx8E+BV_w0nf~awy#mtvdu| zOeWK00__nrxcF*rkAzz<(upnR6(e~5o;)Hv`KMGT z5yhW{O2XEtGdgG_%oL7{e^rvGS|7WjTK@ifm%ml(W4zR~3+Wk|4k;I}T+PZBoWGoN zBR4O~W1)%lqgAIRc7 zFZ!WJMikQOv8W#()<;GgV~uM->-)6pw)*9>zkAGBZXv&jXE(sI&u;2kqsuWKp8w=jSgKTblS+%0>N`aSbV@x><(l!zAgS> zHDmukP1QcGdfw=mjyG_`45 zy%r;)Myt`fc?lm8v`-W2H4=4#yjh?Jj-d9VmFl%SJbHKq9io%i`Gc-!!qAX0jA}?) zd~F>R(^mqXPz+3%v&76;7!&QuoNq;)!e@RKGC}LxT!$5A!I>|dQw9QW1TlowSJbwqHfZ+LLadOa)WQYlS zZw_(4AKQ~pLt1IO*U-V?`<7G(9KLrIQ?e!MCyVJmWX?HWGT8mT45a5q+#V1-_u~Tc zP8V>!s<~|vuCERckT)5@I|IlY#R|4cItsk!CJD3}EryIr60;L9{7fCvbAkF5l*)Uq zYJLBGt?$sk|L|?5x_YbFTgBd|RqU-|ZxwqJtS_r6jHrUG7CT-hxc6Uz*7|NgowIu_ zZ1ow4Ghw^qj#sRd+m~j&^_lciXDg@W(-j+P&aMS1uMiu~vp&D6+7t8gMtQYNYOSre@g3lALBcJv(lr0*_Uc2m1mk-zWmA@OvtKD^d z_K>dOBlDRBJOAw3Z8B_7^=ih=;VU}#I#d{l0&A1z$yv*Krcz=`kjG~2~yVm1V4O$g5Z|zr<<{a48g#5I5hA^q^ zyMM?!z4i^p#fJ}cyB%0!Fc}T5-un6Is+}!AQ-UEgx&Jf0FmPNr+0g8$*d%pM5vNX0ahTjX zr(*7Pa@gXOlVUEp__0(mu-EO%=!{QOEBXo3cpibB4l_+!mlYn;xR=3Oz zZWLS+P8~>;byuF-e`0!A(^>@Y0Zl2)V26g)?Hk~|ybX&+;BNIpH{rs2N6gsY+|hKr z0Pho?H9uhIi*X5u_jl#q+Pv9K(<7ze!KdF)ZZ={`LpE7$AW^^ z^!g6Bxv0V+Nto0^JHZwbdwhKOExn~U&CZn(g`1RgL47?r~ zP_#1ntp|_pE%48FRGC7^8Wc}Rt_x=%Ejm_OO)YVPGlkaZfjiKqzHYboKge469;T1_ zDyrU;C&Fj49)8@*6)c_ewi(N&f6RMmN3AMMmU!8lN&2tH_!fPB)8TGi?uO(Qy{ zwHJz@_`g!`RpZ*%(f6wM9C-egQn+(FFuvPiTIsLjzOpx9T(f=iBQ9fT!cc}^9eww# zOfBN%em&;W+2zHf2ta$@gIm1RBRl5Y{K3DsRo>yDcy_*RVM8GAMcgsPhvv4GY47-t zijc77Q!QT<^5=G&DdC`(=LKJI%oJq=zNaBYs2j|-41TJnt}4bu8WY}Go?ncMkB-yO z!luFyiR;D5_ueP3ID+6X!UkBXjd!ZubbQHn!Vm0}ov7#~OSG{Ryc>}=CnC595{{D6 zZXE0()0a7CJbMS6{2x;sA!iHMu^j{AkJ?tvxV)g=;T1V?Wo z7f+12hbkZ(DGlD^hC&bv3VkafBS_p!4U6gTAmZ2Vyu^&lF!Y0JRVqN*M zSd@A)ihM;z>Z>44`YHYO{LTEz-b|SKx`_XhdY5A}OE9XvQ^~MjC{+M^%gHSA-|WrK zYT{;&d8N6}2c>XTtXKh2S@0Dxx z!|2s0+8~NPg{3$0X=k`0ky4FuIXwqSmm(NI>RrU8>&xBX>liCi@&bmT1ZPf?8B$T) zW+4N#%uwbrHRGwOCMoNkz6D7b>Jp|-e0GJLsSV5CxG#GJl??-oYZ8hcns6KiH2^cV zTjlJr$}yZFY?MPymg&1CITk_LrtxXJoS52G{ESlcYB0?u19U!7vLM>IDQf46IJ6Z@Lvm$GDz<_?Bo!$a7D*CVgZ;Wa- z)8^&2ttdO2y}dc>DOZeS2&c(?QMkSBd?Ux@NA3=$xps#Qk02j+{#+21@h{cyj5PW> zPqZ$i)>2RH-S%6T*Jxc%v;No~aG#5pqAuB%y8XKs$6S9ixIpZ>MH3`4hx0f=m#oGIFPtswTm~nmhYM5Koh^$ zF4=k|@Zi8QB*l552%oO<%f`Ci7}x+)p>*T$A&vdQ1iM_uw}#)@AAz~!eK(bXuI}7# z&v8!v^<`O+X63Fz5fZfNiqa}|2Rio`48AW%bO*V+5ZrfpMwcq*Ww?`^2QKT~F1K;R zY{98R%N-`TM`{1^^e^zt{`F>=LwoRn^3JDKa|1D656itSBA!KIm#$-oLBD%D-(lH} zT@9kmdWsU9FDOTry=7iIdCA!uHTXLlo7{0y&1Il- zclN!n-Er2$c!!HtSAy3Kzq7kFM_ws4;_Bv)=*rRzJsll`{v*y; zw^?ZWI(B|=BqqtbTCxEXuEskVUgTXhlgB-M&*FJNi)GcjrFVubH_hFETw7kyspxFlx8u>M3N7vID;fU+^-1J@(Q_cbPx# zwm&A#2C~Zs-ElNyPo81(ON{Swm#H8sV5mv*V~WZXj6p5?_iLw(oe}Lp^qZ8;ul$Vg zh;!ks#3EVC*w0<{-Cm)J>iu$8smdp-hXGTV{7c8A4qEcwPB-)Oi=i5hJ|Ekd&#H2H zyMw}f2m3f)^Oxrl!2wBw!b_~`(yP2+|Dq;g<+Jb#v~@^mOH*%car?#D+SwDs1--mt z-^#6hAtzP}dhQkPEUiTbo!aIjcnq1TbFw{m+}>Is^ct@bNrF#kjtz|gUGuNh{b^Y0 z-q0lK4m+RqXLg}c_zlWx*Zk9{T+ndm`5)7Uo3&`Mlx(>JD9`J9^L!V4^gqw+^GL|ew>x{;Qsos&);MrG zbjc-bNC&Oe+}>a}C1mktyP(a38{A_pdbUvXbmfE7#q;%Fmf!3SJ0PAB$P$LO3Nas_ADJzAhwxf!Xq-6I- zfVz89KE3E!XlQErc(!zvx>dzVE4tAaJE9jwtEx2!3a`eE2BTt1L|>`9F~##R&h$^5 z6pte_Q@=+L<{MJqi4w{y+<>$1onpdXZqm1in2n9t&3p}0Y#IWQY$Q&(x|E{NCs@JK zcfitoz0*$S6H2$IJF`;{qhf!KjddVvxFCoSae7!_;>vCO^@BuT1l|i9hXTbdHGuy> zWyobjye^SIN_4_xgf-$YY^)hFJ{Y4BjwOxhl8R*M#g?R2JPD5?oQWpf_9orZ$*6{s zGeOA-#wlcEN~j#1g`hkUk&U^;feZ@wAf?DyfwLy#lel;WAAb>*`5=%&l#(bvXHcL@ ztJM9~bN-*Ru~pAGka%OV9F)Q{u!%@1sO|5rIp8^82c;@uRktH`cVpB9U#T0Ide?wx z_88Dp1f(COHvE;krN&vKNb;SI%xyaKXeNCDpl&X8mY-%hmG&B`z65yA<&2wHMyWXJ zC^xfGi3#E`7mWeH&fJ7ya!Z-oR@sAm=6)&D9F$#ga5eFMjLAO2%??3PX=AHrV?0of zJCd19&aT4e01@{HO7*^Rj`2%6ar=b>l$;;pi4WMsvPQ7ARqj^F1wV!S!f6=A&?(oo zlIg(7u!tukXTWagJf-E_C~3~l3JS|F? qw}Lnan4AJ0r%1vnUgoS(_Z8Q{wXS)=Kif4w`~RozZvd;B^}hjBthE~e literal 0 HcmV?d00001 diff --git a/exercises/hferee/6_hanoi/meta.json b/exercises/hferee/6_hanoi/meta.json new file mode 100644 index 0000000..c59c108 --- /dev/null +++ b/exercises/hferee/6_hanoi/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Hanoi tower", + "backward_exercises": ["hferee/5.1_DNA"] +} diff --git a/exercises/hferee/6_hanoi/prelude.ml b/exercises/hferee/6_hanoi/prelude.ml new file mode 100644 index 0000000..2310a81 --- /dev/null +++ b/exercises/hferee/6_hanoi/prelude.ml @@ -0,0 +1 @@ +type tower = L | M | R diff --git a/exercises/hferee/6_hanoi/prepare.ml b/exercises/hferee/6_hanoi/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/6_hanoi/solution.ml b/exercises/hferee/6_hanoi/solution.ml new file mode 100644 index 0000000..bb3c424 --- /dev/null +++ b/exercises/hferee/6_hanoi/solution.ml @@ -0,0 +1,24 @@ +let string_of_tower = function + | L -> "gauche" + | R -> "droite" + | M -> "milieu" + +let move x y = + print_string (string_of_tower x ^ " -> " ^ string_of_tower y); + print_newline() + +let tower3 () = + move L R; + move L M; + move R M; + move L R; + move M L; + move M R; + move L R + +let rec solve_tower_aux l m r = function + | 0 -> () + | n -> solve_tower_aux l r m (n - 1); move l r; solve_tower_aux m l r (n - 1) + +let solve_tower = solve_tower_aux L M R + diff --git a/exercises/hferee/6_hanoi/template.ml b/exercises/hferee/6_hanoi/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/6_hanoi/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/6_hanoi/test.ml b/exercises/hferee/6_hanoi/test.ml new file mode 100644 index 0000000..fcba58f --- /dev/null +++ b/exercises/hferee/6_hanoi/test.ml @@ -0,0 +1,32 @@ + +open Test_lib +open Report + +let exercise = + [ + Section( + [Code "move"], + test_function_2_against_solution [%ty: tower -> tower -> unit] "move" + ~test:test_ignore ~gen:0 + ~test_stdout:io_test_lines [L, M; L, R; M, R; M, L; R, L; R, M] + ); + Section( + [Code "tower3"], + test_function_1_against_solution [%ty: unit -> unit] "tower3" + ~test:test_ignore ~gen:0 + ~test_stdout:io_test_lines [()] + ); + Section( + [Code "solve_tower"], + test_function_1_against_solution [%ty: int -> unit] "solve_tower" + ~test:test_ignore ~gen:0 + ~test_stdout:io_test_lines [0; 1; 2; 3; 4; 5] + ); + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/6_sierpinsky_ascii/descr.md b/exercises/hferee/6_sierpinsky_ascii/descr.md new file mode 100644 index 0000000..c06165a --- /dev/null +++ b/exercises/hferee/6_sierpinsky_ascii/descr.md @@ -0,0 +1,71 @@ +We will represent the famous fractal known as the "Sierpiński triangle" using ASCII characters. + +The fractal is defined by its level of detail. +- Level `0` consists of a single character "*". +- The next level is the simple triangle: + +``` + * +* * +``` + +- Each level is constructed from the previous level by making three copies of the previous level and arranging them in a triangular shape. For example, level 4 looks like this: + +``` + * + * * + * * + * * * * + * * + * * * * + * * * * + * * * * * * * * + * * + * * * * + * * * * + * * * * * * * * + * * * * + * * * * * * * * + * * * * * * * * +* * * * * * * * * * * * * * * * +``` + +--- + +**Question 1** + +Let's start by determining the number of lines and columns required to represent the triangle at level `n`. + +Define a recursive function `size_sierpinsky: int -> int * int` that calculates the height and width of the triangle at a given level. + +--- + +**Question 2** + +Before drawing the fractal, let's represent it with a function. + +Define a function `s: int -> int -> int -> string` such that `s n i j` returns the string (either `" "` or `"*"`) at line `i` and column `j` of the Sierpiński triangle at level `n`. Assume that `i` and `j` are between `0` and the dimensions given by `size_sierpinsky`. + +--- + +**Question 3** + +We now want to draw the Sierpiński triangle. + +Define a function `draw: (int -> int -> string) -> (int * int) -> unit` such that `draw f (l, c)` displays `l` lines and `c` columns, with the characters at line `i` and column `j` given by `f i j`. + +You can use the `print_string` and `print_newline` functions for this. + +For example, `draw (fun x y -> if x = y then "A" else "B") (4, 6)` will display: +``` +ABBBBB +BABBBB +BBABBB +BBBABB +``` + +--- + +**Question 4**: + +Use the previous functions to define the function `sierpinsky: int -> unit` that displays the Sierpiński triangle at a given level. Check what it produces up to `n = 5`. diff --git a/exercises/hferee/6_sierpinsky_ascii/meta.json b/exercises/hferee/6_sierpinsky_ascii/meta.json new file mode 100644 index 0000000..25008dc --- /dev/null +++ b/exercises/hferee/6_sierpinsky_ascii/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 3, + "title": "Sierpinsky in ascii", + "backward_exercises": ["hferee/6_hanoi"] +} diff --git a/exercises/hferee/6_sierpinsky_ascii/prelude.ml b/exercises/hferee/6_sierpinsky_ascii/prelude.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/6_sierpinsky_ascii/prelude.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/6_sierpinsky_ascii/prepare.ml b/exercises/hferee/6_sierpinsky_ascii/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/6_sierpinsky_ascii/solution.ml b/exercises/hferee/6_sierpinsky_ascii/solution.ml new file mode 100644 index 0000000..2f7bdab --- /dev/null +++ b/exercises/hferee/6_sierpinsky_ascii/solution.ml @@ -0,0 +1,26 @@ + +let rec size_sierpinsky n = if n <= 0 then (1, 1) else + let (h, w) = size_sierpinsky (n - 1) in (2 * h, 2 * w + 1) + +let rec s n i j : string = if n <= 0 then "*" else + let (h, w) = size_sierpinsky (n - 1) in + let w' = (w + 1) / 2 (* 2w' + w = 2w + 1 *) + and n' = n - 1 in + if i < h then (* top triangle *) + if j < w' || j - w' >= w then " " else s n' i (j - w') + else (* bottom triangles *) + let i' = i - h in + if j < w then s n' i' j + else if j = w then " " + else s n' i' (j - w - 1) + +(* volontairement pas tailerc, pour voir si ça passe *) +let draw f (l, c) = + let rec aux i j = + if i >= l then () (* end *) + else if j >= c then begin print_newline(); aux (i + 1) 0 end (* end of line *) + else begin print_string (f i j); aux i (j + 1) end + in aux 0 0 + + +let rec sierpinsky n = draw (s n) (size_sierpinsky n) diff --git a/exercises/hferee/6_sierpinsky_ascii/template.ml b/exercises/hferee/6_sierpinsky_ascii/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/6_sierpinsky_ascii/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/6_sierpinsky_ascii/test.ml b/exercises/hferee/6_sierpinsky_ascii/test.ml new file mode 100644 index 0000000..53af62d --- /dev/null +++ b/exercises/hferee/6_sierpinsky_ascii/test.ml @@ -0,0 +1,52 @@ + +open Test_lib +open Report + +let sample_int () = abs (sample_int ()) + +let all_char i j = String.make 1 (Char.chr (i + 16 * j)) + + +let sample_s() = + let n = sample_int() in + let (h, w) = Solution.size_sierpinsky n in + let h = sample_int() mod h + and w = sample_int() mod w in + (n, h, w) + + +let ints = [0; 1; 2; 3; 4; 5] +let exercise = + [ + Section( + [Code "size_sierpinsky"], + test_function_1_against_solution [%ty: int -> int * int] + "size_sierpinsky" ~gen:0 ints + ); + Section( + [Code "s"], + test_function_3_against_solution [%ty: int -> int -> int -> string] + "s" ~gen:30 ~sampler:sample_s [] + ); + Section( + [Code "draw"], + test_function_2_against_solution + [%ty: (int -> int -> string) -> (int * int) -> unit] "draw" + ~test:test_ignore ~gen:0 ~test_stdout:io_test_lines + [(all_char, (16, 16))] + ); + Section( + [Code "sierpinsky"], + test_function_1_against_solution [%ty: int -> unit] + "sierpinsky" ~test:test_ignore ~gen:0 + ~test_stdout:io_test_lines ints + ); + + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/7_lists/descr.md b/exercises/hferee/7_lists/descr.md new file mode 100644 index 0000000..e20f902 --- /dev/null +++ b/exercises/hferee/7_lists/descr.md @@ -0,0 +1,41 @@ +Condider the following type of integer lists : + +```ocaml +type liste = Nothing | OneMore of int * liste +``` + +The goal here is to master basic functions on lists. + +--- + +**Question 1**: + +Define the function `hd` that calculates the head of a list. We will return an arbitrary value in case the list is empty. + +Similarly, define the function `tl` that calculates the tail of a list. + +--- + +**Question 2**: + +Define the functions `length` and `sum_list` that respectively calculate the length and the sum of the elements in a list. + +--- + +**Question 3**: + +Define the `concat` function that concatenates two lists, as well as the `rev` function that reverses the order of a list. + +--- + +**Question 4**: + +Define the `mem` function that takes an integer and a list and returns a boolean indicating whether the integer belongs to the list. + +Similarly, define the `find_first` and `find_last` functions that respectively return the first and last position of an integer in a list. We will return `-1` if the integer is not found. + +--- + +**Question 5**: + +Define a function `partition: (int -> bool) -> liste -> liste * liste` such that `partition p l = (l1, l2)` where `l1` (respectively `l2`) contains the elements from `l` that satisfy the predicate `p` (respectively do not satisfy `p`), in the same order.w diff --git a/exercises/hferee/7_lists/meta.json b/exercises/hferee/7_lists/meta.json new file mode 100644 index 0000000..f5b8776 --- /dev/null +++ b/exercises/hferee/7_lists/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Lists", + "backward_exercises": ["smelodesousa/F3/3-dragon-greatness"] +} diff --git a/exercises/hferee/7_lists/prelude.ml b/exercises/hferee/7_lists/prelude.ml new file mode 100644 index 0000000..8a6a1be --- /dev/null +++ b/exercises/hferee/7_lists/prelude.ml @@ -0,0 +1 @@ +type liste = Nothing | OneMore of int * liste diff --git a/exercises/hferee/7_lists/prepare.ml b/exercises/hferee/7_lists/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/7_lists/solution.ml b/exercises/hferee/7_lists/solution.ml new file mode 100644 index 0000000..5bd383f --- /dev/null +++ b/exercises/hferee/7_lists/solution.ml @@ -0,0 +1,56 @@ +let hd = function + | Nothing -> 42 + | OneMore (h, _) -> h + +let tl = function + | Nothing -> Nothing + | OneMore (_, t) -> t + +let rec length = function + | Nothing -> 0 + | OneMore (_, t) -> 1 + length t + +let rec sum_list = function + | Nothing -> 0 + | OneMore (h, t) -> h + sum_list t + +let rec concat l1 l2 = match l1 with + | Nothing -> l2 + | OneMore (h, t) -> OneMore (h, concat t l2) + +let rev = + let rec rev_aux rl = function + | Nothing -> rl + | OneMore (h, t) -> rev_aux (OneMore (h, rl)) t + in rev_aux Nothing + +let rec mem e = function + | Nothing -> false + | OneMore (h, t) -> h = e || mem e t + + +let rec find_first e = function + | Nothing -> -1 + | OneMore (h, t) -> + if h = e + then 0 + else + let i = find_first e t in + if i = -1 then -1 + else i + 1 + + +let rec find_last e = function + | Nothing -> -1 + | OneMore (h, t) -> + let i = find_last e t in + if i = -1 then + if h = e then 0 else -1 + else i + 1 + +let rec partition p = function + | Nothing -> Nothing, Nothing + | OneMore (h, t) -> + let (l1, l2) = partition p t in + if p h then OneMore (h, l1), l2 + else l1, OneMore (h, l2) diff --git a/exercises/hferee/7_lists/template.ml b/exercises/hferee/7_lists/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/7_lists/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/7_lists/test.ml b/exercises/hferee/7_lists/test.ml new file mode 100644 index 0000000..c16f756 --- /dev/null +++ b/exercises/hferee/7_lists/test.ml @@ -0,0 +1,62 @@ + +open Test_lib +open Report +let test_listes = [ + Nothing; + OneMore(0, Nothing); + OneMore(0, OneMore(1, Nothing)); + OneMore(0, OneMore(1, OneMore(2, OneMore(3, Nothing)))); + OneMore(0, OneMore(1, OneMore(0, OneMore(1, Nothing)))) + ] + +let product l1 l2 = + List.fold_left + (fun x a -> List.fold_left + (fun y b -> (a, b) :: y) + x + l2) + [] + l1 + + +let test_listes2 = product test_listes test_listes + +let test_nonempty_listes = List.tl test_listes + +let exercise = + [ + Section ( [ Code "hd" ], + test_function_1_against_solution [%ty : liste -> int] ~gen:0 "hd" + test_nonempty_listes); + Section ( [ Code "tl" ], + test_function_1_against_solution [%ty : liste -> liste] ~gen:0 "tl" + test_listes); + Section ( [ Code "length" ], + test_function_1_against_solution [%ty : liste -> int] ~gen:0 "length" + test_listes); + Section ( [ Code "sum_list" ], + test_function_1_against_solution [%ty : liste -> int] ~gen:0 "sum_list" test_listes); + Section ( [ Code "concat" ], + test_function_2_against_solution [%ty : liste -> liste -> liste] ~gen:0 "concat" + test_listes2); + Section ( [ Code "rev" ], + test_function_1_against_solution [%ty : liste -> liste] ~gen:0 "rev" test_listes); + Section ( [ Code "mem" ], + test_function_2_against_solution [%ty : int -> liste -> bool] ~gen:0 "mem" + (product [0; 1; 42] test_listes)); + Section ( [ Code "find_first" ], + test_function_2_against_solution [%ty : int -> liste -> int] ~gen:0 "find_first" + (product [0; 1; 42] test_listes)); + Section ( [ Code "find_last" ], + test_function_2_against_solution [%ty : int -> liste -> int] ~gen:0 "find_last" + (product [0; 1; 42] test_listes)); + Section ( [ Code "partition" ], + test_function_2_against_solution [%ty : (int -> bool) -> liste -> liste * liste] ~gen:0 "partition" + (product [fun x -> x mod 2 = 0] test_listes)); + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> exercise + diff --git a/exercises/hferee/7_lists_binary_encoding/descr.md b/exercises/hferee/7_lists_binary_encoding/descr.md new file mode 100644 index 0000000..5e3a7ff --- /dev/null +++ b/exercises/hferee/7_lists_binary_encoding/descr.md @@ -0,0 +1,41 @@ +We want to represent positive integers using a binary representation. + +This time we will use a type with boolean elements: `false` represents the bit `0` and `true` represents the bit `1`. +```ocaml +type liste_bool = Nothing | OneMore of bool * liste +``` +In the following, for simplicity, we will refer to the booleans by their associated bits. + +More formally, an integer is represented by a non-empty list of booleans where the head is the least significant bit. + +For example, the list `OneMore(false, OneMore(true, OneMore(true, Nothing)))` represents the number `0*1 + 1*2 + 1*2² = 6`. + +**Note**: In order for each number to have a unique binary representation, we disallow any _leading zeros_. In other words, the most significant bit is always `1` (`true`), except for the number `0`. + +--- + +**Question 1**: +Write a function `is_binary: liste_bool -> bool` that determines whether a list represents a number written in base `2`. + +**Question 2**: +Write a function `encode: int -> liste_bool` that calculates the binary representation of a positive integer (the behavior of `encode` for negative numbers is not specified). + +**Question 3**: +Write the function `decode: liste_bool -> int` which, conversely, calculates the integer corresponding to a valid binary representation, taking into account the following note. + +For this, we can observe that +```ocaml +2⁰ * a₀ + 2¹ * a₁ + 2² * a₂ + 2³ * a₃ + 2⁴ * a₄ + 2⁵ * a₅ + 2⁶ * a₆ + 2⁷ * a₇ +``` +can also be written as +```ocaml +a₀ + 2 * (a₁ + 2 * (a₂ + 2 * (a₃ + 2 * (a₄ + 2 * (a₅ + 2 * (a₆ + 2 * a₇)))))) +``` + +**Question 4**: +Define the function `plus_bin: liste_bool -> liste_bool -> liste_bool` that calculates the addition in binary representation. + +We can observe that, similar to decimal addition, binary addition can be done by starting from the least significant bits and potentially propagating a carry. + +**Bonus**: +Implement multiplication, considering its efficiency. diff --git a/exercises/hferee/7_lists_binary_encoding/meta.json b/exercises/hferee/7_lists_binary_encoding/meta.json new file mode 100644 index 0000000..efd9c3a --- /dev/null +++ b/exercises/hferee/7_lists_binary_encoding/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Binary encoding of integers", + "backward_exercises": ["hferee/7_lists"] +} diff --git a/exercises/hferee/7_lists_binary_encoding/prelude.ml b/exercises/hferee/7_lists_binary_encoding/prelude.ml new file mode 100644 index 0000000..442a68e --- /dev/null +++ b/exercises/hferee/7_lists_binary_encoding/prelude.ml @@ -0,0 +1 @@ +type liste_bool = Nothing | OneMore of bool * liste_bool diff --git a/exercises/hferee/7_lists_binary_encoding/prepare.ml b/exercises/hferee/7_lists_binary_encoding/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/7_lists_binary_encoding/solution.ml b/exercises/hferee/7_lists_binary_encoding/solution.ml new file mode 100644 index 0000000..5ac60ba --- /dev/null +++ b/exercises/hferee/7_lists_binary_encoding/solution.ml @@ -0,0 +1,33 @@ +let rec is_binary = function + (* non vide *) + | Nothing -> false + (* zéro et un marchent *) + | OneMore(_, Nothing) -> true + (* seul zéro termine par zero *) + | OneMore(_, OneMore(false, Nothing)) -> false + | OneMore(h, t) -> is_binary t + +let rec encode = function + | n when n < 0 -> failwith "négatif" + | 0 -> OneMore(false, Nothing) + | 1 -> OneMore(true, Nothing) + | n -> OneMore(n mod 2 = 1 , encode (n / 2)) + +let rec decode = function + | Nothing -> 0 + | OneMore(h, t) -> (if h then 1 else 0) + 2 * decode t + +let half_adder x y = + let xandy = x && y in + (x || y) && (not xandy), xandy + + let plus_bin = + let rec plus_carry c l1 l2 = + match c, l1, l2 with + | false, l, Nothing | false, Nothing, l -> l + | true, l, Nothing | true, Nothing, l -> plus_carry false (OneMore(true, Nothing)) l + | c, OneMore(x, l1), OneMore(y, l2) -> + let b1, c1 = half_adder c x in + let b, c2 = half_adder b1 y in + OneMore(b, plus_carry (c1 || c2) l1 l2) + in plus_carry false diff --git a/exercises/hferee/7_lists_binary_encoding/template.ml b/exercises/hferee/7_lists_binary_encoding/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/7_lists_binary_encoding/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/7_lists_binary_encoding/test.ml b/exercises/hferee/7_lists_binary_encoding/test.ml new file mode 100644 index 0000000..c917241 --- /dev/null +++ b/exercises/hferee/7_lists_binary_encoding/test.ml @@ -0,0 +1,49 @@ + +open Test_lib +open Report + +let sample_pos () = abs (sample_int()) +let sample_bin () = Solution.encode (abs (sample_int())) +let sample_bin2 () = sample_bin(), sample_bin() + +let test_ints = [0;1;2;3;4;5;6;7;8; 123346236] +let test_bins2 = + List.flatten + (List.map + (fun x -> List.map (fun y -> Solution.encode x, Solution.encode y) test_ints) + test_ints) + + +let exercise = + [ + Section ( + [ Code "is_binary" ], + test_function_1_against_solution [%ty : liste_bool -> bool] + ~gen:0 "is_binary" [Nothing; + OneMore(true, Nothing); + OneMore(false, Nothing); + OneMore(true, OneMore(false, Nothing)); + OneMore(true, OneMore(true, Nothing)); + OneMore(true, OneMore(true, OneMore(true, Nothing))); + ] + ); + Section ( + [ Code "encode" ], test_function_1_against_solution [%ty : int -> liste_bool] + ~gen:10 ~sampler:sample_pos "encode" test_ints + ); + Section ( + [ Code "decode" ], test_function_1_against_solution [%ty : liste_bool -> int] "decode" + ~gen:10 ~sampler:sample_bin (List.map Solution.encode test_ints) + ); + Section ( + [ Code "plus_bin" ], test_function_2_against_solution [%ty : liste_bool -> liste_bool -> liste_bool] + "plus_bin" ~gen:10 ~sampler:sample_bin2 test_bins2 + ) +] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/8_fold/descr.md b/exercises/hferee/8_fold/descr.md new file mode 100644 index 0000000..47ac046 --- /dev/null +++ b/exercises/hferee/8_fold/descr.md @@ -0,0 +1,30 @@ +Throughout this exercise, we will consider a type `liste` for a list of integers and `fold_right: (int -> 'a -> 'a) -> liste -> 'a -> 'a` as its right folding function. + +**Note**: In the following exercise, you won't need to know the precise implementation of lists. You should use the `fold_right` function with the appropriate arguments. + +--- + +**Question 1**: + +Using the `min` and `max` functions for integers, define the functions `list_min` and `list_max` that respectively calculate the minimum and maximum of a list. In the case of an empty list, you should return `min_int` or `max_int`. + +--- + +**Question 2**: + +Define the functions `count_if`, `forall`, and `exists` such that if `p` is of type `int -> bool` and `l` is a list, then: + - `count_if p l` returns the number of elements in `l` for which `p` is `true`. + - `forall p l` returns `true` if and only if `p` is true for all elements of `l`. + - `exists p l` is true if `p` is true for at least one element of `l`. + +--- + +**Question 3**: + +Using `exists`, define the function `mem: int -> liste -> bool` (for *member*) that determines whether an element is contained in a list. + +--- + +**Bonus**: + +Define the functions `find_first` and `find_last` that return the first and last occurrences, respectively, satisfying a test `p`. diff --git a/exercises/hferee/8_fold/meta.json b/exercises/hferee/8_fold/meta.json new file mode 100644 index 0000000..a37816d --- /dev/null +++ b/exercises/hferee/8_fold/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Applications of the fold_right function", + "backward_exercises": ["hferee/7_lists_binary_encoding"] +} diff --git a/exercises/hferee/8_fold/prelude.ml b/exercises/hferee/8_fold/prelude.ml new file mode 100644 index 0000000..9be7971 --- /dev/null +++ b/exercises/hferee/8_fold/prelude.ml @@ -0,0 +1,5 @@ +type liste = Nothing | OneMore of int * liste + +let rec fold_right f xs acc = match xs with + | Nothing -> acc + | OneMore (x, xs) -> f x (fold_right f xs acc) diff --git a/exercises/hferee/8_fold/prepare.ml b/exercises/hferee/8_fold/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/8_fold/solution.ml b/exercises/hferee/8_fold/solution.ml new file mode 100644 index 0000000..d9b36cb --- /dev/null +++ b/exercises/hferee/8_fold/solution.ml @@ -0,0 +1,22 @@ +let list_min l = fold_right min l max_int + +let list_max l = fold_right max l min_int + + +let count_if p l = fold_right (fun x acc -> acc + if p x then 1 else 0) l 0 + +let forall p l = fold_right (fun e acc -> p e && acc) l true + +let exists p l = fold_right (fun e acc -> p e || acc) l false + +let mem x = exists (( = ) x) + + +(* bonus *) +let find_first p l = fold_right (fun e acc -> if p e then 0 else if acc = - 1 + then -1 else acc + 1) l (-1) + +let find_last p l = fold_right (fun e acc -> if acc >= 0 then acc + 1 else if p e + then 0 else -1) l (-1) + + diff --git a/exercises/hferee/8_fold/template.ml b/exercises/hferee/8_fold/template.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/8_fold/test.ml b/exercises/hferee/8_fold/test.ml new file mode 100644 index 0000000..5891304 --- /dev/null +++ b/exercises/hferee/8_fold/test.ml @@ -0,0 +1,86 @@ + +open Test_lib +open Report +open Parsetree +open Longident + +let test_construct c (e : expression) = match e.pexp_desc with +| Pexp_construct (id, _) | Pexp_ident (id) when id.txt = Lident c -> true +| _ -> false + +let check name f n = + let count = ref 0 in + let reports = find_binding code_ast name (ast_check_expr ~on_expression: + (fun v -> if test_construct f v then incr count; [])) + in + if !count <> n then + let msg = if n = 0 then "Ne pas utiliser " else "Utiliser " in + [Message ([Text (msg ^ f)], Failure)] + else [] + +let test name t constraints = + Section([Code name], t @ + List.fold_left (fun acc f -> acc @ check name f 1) [] constraints @ + (* ne jamais utiliser les constructeurs de type *) + List.fold_left (fun acc f -> acc @ check name f 0) [] ["Nothing"; "OneMore"] + ) + +let test_list = OneMore(0, OneMore(1, OneMore(min_int, OneMore(max_int, OneMore(2, Nothing))))) +let test_fun = fun x -> x mod 2 = 0 +let exercise = + [ + test "list_min" + (test_function_1_against_solution ~gen:0 [%ty : liste -> int] "list_min" + [Nothing; test_list]) + ["min"; "fold_right"]; + + test "list_max" + (test_function_1_against_solution ~gen:0 [%ty : liste -> int] + "list_max" [Nothing; test_list]) + ["max"; "fold_right"]; + test "count_if" + (test_function_2_against_solution ~gen:0 [%ty : (int -> bool) + -> liste -> int] "count_if" + [test_fun, Nothing; test_fun, test_list]) + ["fold_right"]; + test "forall" + (test_function_2_against_solution ~gen:0 [%ty : (int -> bool) -> liste -> bool] "forall" + [test_fun, Nothing; test_fun, test_list; (<>) 4, test_list]) + ["fold_right"]; + test "exists" + (test_function_2_against_solution ~gen:0 [%ty : (int -> bool) -> liste -> bool] "exists" + [test_fun, Nothing; test_fun, test_list; (=) 4, test_list]) + ["fold_right"]; + test "mem" + (test_function_2_against_solution ~gen:0 [%ty : int -> liste -> bool] "mem" + [0, Nothing; 0, test_list; 4, test_list]) + ["exists"]; + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + +(* +let list_min l + +let list_max l + +count_if p l + +forall p l + +exists p l + +mem x + +(* bonus *) +filter f l +map f l +find_first p l + +find_last p l + *) + diff --git a/exercises/hferee/8_sieve/descr.md b/exercises/hferee/8_sieve/descr.md new file mode 100644 index 0000000..b154170 --- /dev/null +++ b/exercises/hferee/8_sieve/descr.md @@ -0,0 +1,23 @@ +The Sieve of Eratosthenes is an algorithm that efficiently calculates all prime numbers smaller than a given number `n`. + +The algorithm works as follows: +1. We build a list of numbers from 2 to `n`. +2. If `k` is the next number in the list, then it is prime, and we remove all multiples of `k` (except `k`) from the rest of the list. +3. We repeat step 2 as long as there are numbers remaining to be processed. + +**Question 1**: +Write a function `range: int -> liste` that, given an integer `n`, constructs the ordered list of numbers from `2` to `n`. + +**Question 2**: +Define the function `filter: (int -> bool) -> liste -> liste` that filters the elements of a list based on a function with a boolean output. + +**Question 3**: +Implement the sieve algorithm in a recursive function `sieve`. + +
+ Hint + Use the `filter` function. +
+ +**Bonus**: +Calculate `sieve 100000` in less than 20 seconds. diff --git a/exercises/hferee/8_sieve/meta.json b/exercises/hferee/8_sieve/meta.json new file mode 100644 index 0000000..a63800f --- /dev/null +++ b/exercises/hferee/8_sieve/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Sieve of Eratosthenes", + "backward_exercises": ["hferee/8_fold"] +} diff --git a/exercises/hferee/8_sieve/prelude.ml b/exercises/hferee/8_sieve/prelude.ml new file mode 100644 index 0000000..02ea940 --- /dev/null +++ b/exercises/hferee/8_sieve/prelude.ml @@ -0,0 +1,3 @@ +type liste = Nothing | OneMore of (int * liste) + + diff --git a/exercises/hferee/8_sieve/prepare.ml b/exercises/hferee/8_sieve/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/8_sieve/solution.ml b/exercises/hferee/8_sieve/solution.ml new file mode 100644 index 0000000..24b01d2 --- /dev/null +++ b/exercises/hferee/8_sieve/solution.ml @@ -0,0 +1,36 @@ + +let range n = + let rec aux k acc = + if k < 2 then acc + else aux (k - 1) (OneMore(k, acc)) + in aux n Nothing + + +let rev = + let rec aux acc = function + | Nothing -> acc + | OneMore(h, t) -> aux (OneMore(h, acc)) t + in aux Nothing + +(* non tailrec volontairement *) +let filter f = + let rec aux acc = function + | Nothing -> rev acc + | OneMore(h, t) -> if f h then aux (OneMore(h, acc)) t + else aux acc t + in aux Nothing + +let sieve n = + let non_div x y = y mod x <> 0 in + let rec aux = function + | Nothing -> Nothing + | OneMore(h, t) -> OneMore(h, filter (non_div h) t) + in aux (range n) + +let sieve n = + let non_div x y = y mod x <> 0 in + let rec aux acc = function + | Nothing -> acc + | OneMore(h, t) -> aux (OneMore(h, acc)) (filter (non_div h) t) + in rev(aux Nothing (range n)) + diff --git a/exercises/hferee/8_sieve/template.ml b/exercises/hferee/8_sieve/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/8_sieve/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/8_sieve/test.ml b/exercises/hferee/8_sieve/test.ml new file mode 100644 index 0000000..e69b4c5 --- /dev/null +++ b/exercises/hferee/8_sieve/test.ml @@ -0,0 +1,32 @@ + +open Test_lib +open Report + +let exercise = + [ + Section( + [Code "range"], + test_function_1_against_solution [%ty : int -> liste] ~gen:0 "range" [0; + 1; + 2; + 8] + ); + Section( + [Code "filter"], + test_function_2_against_solution [%ty : (int -> bool) -> liste -> liste] + "filter" ~gen:0 [(fun x -> x mod 2 <> 0), Nothing; (fun x -> x mod 2 <> + 0), Solution.range 15] + ); + Section( + [Code "sieve"], + test_function_1_against_solution [%ty: int -> liste] "sieve" ~gen:0 [0; 1; 2; 5; + 100] + ) + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + diff --git a/exercises/hferee/8_sort/descr.md b/exercises/hferee/8_sort/descr.md new file mode 100644 index 0000000..662ea23 --- /dev/null +++ b/exercises/hferee/8_sort/descr.md @@ -0,0 +1,10 @@ +We want to sort a list of integers in ascending order. + +**Question 1**: +Define a recursive function `insert: int -> liste -> liste` that inserts an element into a list in such a way that the output list is sorted, assuming the input list was already sorted. + +**Question 2**: +Using `fold_right` and `insert`, define a function `sort` that sorts a list by successively inserting all its elements. + +**Bonus**: +Using `fold_right`, define a function `test_sort: liste -> bool` that indicates whether a list is sorted or not. diff --git a/exercises/hferee/8_sort/meta.json b/exercises/hferee/8_sort/meta.json new file mode 100644 index 0000000..42739cf --- /dev/null +++ b/exercises/hferee/8_sort/meta.json @@ -0,0 +1,7 @@ +{ + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Sort by insertion", + "backward_exercises": ["hferee/8_sieve"] +} diff --git a/exercises/hferee/8_sort/prelude.ml b/exercises/hferee/8_sort/prelude.ml new file mode 100644 index 0000000..ffe55b7 --- /dev/null +++ b/exercises/hferee/8_sort/prelude.ml @@ -0,0 +1,6 @@ +type liste = Nothing | OneMore of (int * liste) + +let rec fold_right f xs acc = match xs with + | Nothing -> acc + | OneMore (x, xs) -> f x (fold_right f xs acc) + diff --git a/exercises/hferee/8_sort/prepare.ml b/exercises/hferee/8_sort/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/hferee/8_sort/solution.ml b/exercises/hferee/8_sort/solution.ml new file mode 100644 index 0000000..2744ad3 --- /dev/null +++ b/exercises/hferee/8_sort/solution.ml @@ -0,0 +1,13 @@ +(* insère x dans une liste triée l *) +let rec insert x = function + | Nothing -> OneMore(x, Nothing) + | OneMore(h, t) -> + if h < x + then OneMore(h, insert x t) + else OneMore(x, OneMore(h, t)) + +let sort l = fold_right insert l Nothing + +let test_sort l = + snd(fold_right (fun e (mi, est_triee) -> (min e mi, est_triee && e <= mi)) l + (max_int, true)) diff --git a/exercises/hferee/8_sort/template.ml b/exercises/hferee/8_sort/template.ml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/exercises/hferee/8_sort/template.ml @@ -0,0 +1 @@ + diff --git a/exercises/hferee/8_sort/test.ml b/exercises/hferee/8_sort/test.ml new file mode 100644 index 0000000..f2d4268 --- /dev/null +++ b/exercises/hferee/8_sort/test.ml @@ -0,0 +1,48 @@ + +open Test_lib +open Report +open Parsetree +open Longident + +let test_construct c (e : expression) = match e.pexp_desc with +| Pexp_construct (id, _) | Pexp_ident (id) when id.txt = Lident c -> true +| _ -> false + +let check name f n = + let count = ref 0 in + let reports = find_binding code_ast name (ast_check_expr ~on_expression: + (fun v -> if test_construct f v then incr count; [])) + in + if !count <> n then + let msg = if n = 0 then "Ne pas utiliser " else "Utiliser " in + [Message ([Text (msg ^ f)], Failure)] + else [] + +let test_list = OneMore(0, OneMore(1, OneMore(min_int, OneMore(max_int, OneMore(2, Nothing))))) +let sorted_list = OneMore(min_int, OneMore(-1, OneMore(1, OneMore(7, + OneMore(max_int, Nothing))))) +let test name t constraints = + Section([Code name], t @ + List.fold_left (fun acc f -> acc @ check name f 1) [] constraints + ) + +let test_fun = fun x -> x mod 2 = 0 +let exercise = + [ + test "insert" + (test_function_2_against_solution [%ty : int -> liste -> liste] ~gen:0 + "insert" [7, Nothing; 3, sorted_list]) + []; + + test "sort" + (test_function_1_against_solution [%ty : liste -> liste] ~gen:0 "sort" + [Nothing; test_list]) + ["insert"; "fold_right"] + ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ + fun () -> + exercise + From 71c52ab24077d9fcd9a19700ed974ca97946b4f7 Mon Sep 17 00:00:00 2001 From: Mohamed Hernouf Date: Mon, 5 Jun 2023 15:29:18 +0200 Subject: [PATCH 2/8] =?UTF-8?q?Adding=20exercises=20by=20Sim=C3=A3o=20Melo?= =?UTF-8?q?=20de=20Susa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exercises/smelodesousa/F1/1-errors/descr.md | 6 + exercises/smelodesousa/F1/1-errors/meta.json | 9 + exercises/smelodesousa/F1/1-errors/prelude.ml | 0 exercises/smelodesousa/F1/1-errors/prepare.ml | 0 .../smelodesousa/F1/1-errors/solution.ml | 7 + .../smelodesousa/F1/1-errors/template.ml | 7 + exercises/smelodesousa/F1/1-errors/test.ml | 35 +++ .../smelodesousa/F1/1-fun-calls/descr.md | 10 + .../smelodesousa/F1/1-fun-calls/meta.json | 9 + .../smelodesousa/F1/1-fun-calls/prelude.ml | 0 .../smelodesousa/F1/1-fun-calls/prepare.ml | 2 + .../smelodesousa/F1/1-fun-calls/solution.ml | 1 + .../smelodesousa/F1/1-fun-calls/template.ml | 1 + exercises/smelodesousa/F1/1-fun-calls/test.ml | 12 + exercises/smelodesousa/F1/1-future/descr.md | 39 ++++ exercises/smelodesousa/F1/1-future/meta.json | 9 + exercises/smelodesousa/F1/1-future/prelude.ml | 0 exercises/smelodesousa/F1/1-future/prepare.ml | 2 + .../smelodesousa/F1/1-future/solution.ml | 5 + .../smelodesousa/F1/1-future/template.ml | 5 + exercises/smelodesousa/F1/1-future/test.ml | 51 +++++ exercises/smelodesousa/F1/1-mistery/descr.md | 34 +++ exercises/smelodesousa/F1/1-mistery/meta.json | 9 + .../smelodesousa/F1/1-mistery/prelude.ml | 0 .../smelodesousa/F1/1-mistery/prepare.ml | 4 + .../smelodesousa/F1/1-mistery/solution.ml | 5 + .../smelodesousa/F1/1-mistery/template.ml | 5 + exercises/smelodesousa/F1/1-mistery/test.ml | 55 +++++ .../smelodesousa/F1/1-true-false/descr.md | 23 ++ .../smelodesousa/F1/1-true-false/meta.json | 12 + .../smelodesousa/F1/1-true-false/prelude.ml | 0 .../smelodesousa/F1/1-true-false/prepare.ml | 1 + .../smelodesousa/F1/1-true-false/solution.ml | 4 + .../smelodesousa/F1/1-true-false/template.ml | 4 + .../smelodesousa/F1/1-true-false/test.ml | 29 +++ exercises/smelodesousa/F1/1-tuples/descr.md | 11 + exercises/smelodesousa/F1/1-tuples/meta.json | 12 + exercises/smelodesousa/F1/1-tuples/prelude.ml | 0 exercises/smelodesousa/F1/1-tuples/prepare.ml | 1 + .../smelodesousa/F1/1-tuples/solution.ml | 1 + .../smelodesousa/F1/1-tuples/template.ml | 1 + exercises/smelodesousa/F1/1-tuples/test.ml | 8 + .../smelodesousa/F1/1-type-error/descr.md | 66 ++++++ .../smelodesousa/F1/1-type-error/meta.json | 12 + .../smelodesousa/F1/1-type-error/prelude.ml | 0 .../smelodesousa/F1/1-type-error/prepare.ml | 1 + .../smelodesousa/F1/1-type-error/solution.ml | 7 + .../smelodesousa/F1/1-type-error/template.ml | 7 + .../smelodesousa/F1/1-type-error/test.ml | 48 ++++ .../smelodesousa/F1/1-type-value/descr.md | 84 +++++++ .../smelodesousa/F1/1-type-value/meta.json | 9 + .../smelodesousa/F1/1-type-value/prelude.ml | 0 .../smelodesousa/F1/1-type-value/prepare.ml | 2 + .../smelodesousa/F1/1-type-value/solution.ml | 17 ++ .../smelodesousa/F1/1-type-value/template.ml | 17 ++ .../smelodesousa/F1/1-type-value/test.ml | 70 ++++++ exercises/smelodesousa/F1/1-type/descr.md | 12 + exercises/smelodesousa/F1/1-type/meta.json | 9 + exercises/smelodesousa/F1/1-type/prelude.ml | 0 exercises/smelodesousa/F1/1-type/prepare.ml | 0 exercises/smelodesousa/F1/1-type/solution.ml | 15 ++ exercises/smelodesousa/F1/1-type/template.ml | 15 ++ exercises/smelodesousa/F1/1-type/test.ml | 144 ++++++++++++ exercises/smelodesousa/F1/1-type2/descr.md | 15 ++ exercises/smelodesousa/F1/1-type2/meta.json | 12 + exercises/smelodesousa/F1/1-type2/prelude.ml | 0 exercises/smelodesousa/F1/1-type2/prepare.ml | 2 + exercises/smelodesousa/F1/1-type2/solution.ml | 3 + exercises/smelodesousa/F1/1-type2/template.ml | 3 + exercises/smelodesousa/F1/1-type2/test.ml | 16 ++ .../smelodesousa/F1/1-what-type/descr.md | 38 ++++ .../smelodesousa/F1/1-what-type/meta.json | 9 + .../smelodesousa/F1/1-what-type/prelude.ml | 0 .../smelodesousa/F1/1-what-type/prepare.ml | 2 + .../smelodesousa/F1/1-what-type/solution.ml | 9 + .../smelodesousa/F1/1-what-type/template.ml | 9 + exercises/smelodesousa/F1/1-what-type/test.ml | 42 ++++ .../smelodesousa/F1/1-what-type2/descr.md | 32 +++ .../smelodesousa/F1/1-what-type2/meta.json | 9 + .../smelodesousa/F1/1-what-type2/prelude.ml | 0 .../smelodesousa/F1/1-what-type2/prepare.ml | 2 + .../smelodesousa/F1/1-what-type2/solution.ml | 7 + .../smelodesousa/F1/1-what-type2/template.ml | 7 + .../smelodesousa/F1/1-what-type2/test.ml | 29 +++ .../F3/3-a-historic-algorithm/descr.md | 27 +++ .../F3/3-a-historic-algorithm/meta.json | 12 + .../F3/3-a-historic-algorithm/prelude.ml | 0 .../F3/3-a-historic-algorithm/prepare.ml | 0 .../F3/3-a-historic-algorithm/solution.ml | 12 + .../F3/3-a-historic-algorithm/template.ml | 1 + .../F3/3-a-historic-algorithm/test.ml | 69 ++++++ exercises/smelodesousa/F3/3-catalan/descr.md | 34 +++ exercises/smelodesousa/F3/3-catalan/meta.json | 9 + .../smelodesousa/F3/3-catalan/prelude.ml | 0 .../smelodesousa/F3/3-catalan/prepare.ml | 0 .../smelodesousa/F3/3-catalan/solution.ml | 4 + .../smelodesousa/F3/3-catalan/template.ml | 2 + exercises/smelodesousa/F3/3-catalan/test.ml | 32 +++ .../F3/3-collatz-hailstones/descr.md | 37 ++++ .../F3/3-collatz-hailstones/meta.json | 9 + .../F3/3-collatz-hailstones/prelude.ml | 0 .../F3/3-collatz-hailstones/prepare.ml | 0 .../F3/3-collatz-hailstones/solution.ml | 4 + .../F3/3-collatz-hailstones/template.ml | 1 + .../F3/3-collatz-hailstones/test.ml | 38 ++++ .../smelodesousa/F3/3-dichotomy/descr.md | 47 ++++ .../smelodesousa/F3/3-dichotomy/meta.json | 13 ++ .../smelodesousa/F3/3-dichotomy/prelude.ml | 0 .../smelodesousa/F3/3-dichotomy/prepare.ml | 0 .../smelodesousa/F3/3-dichotomy/solution.ml | 8 + .../smelodesousa/F3/3-dichotomy/template.ml | 2 + exercises/smelodesousa/F3/3-dichotomy/test.ml | 85 +++++++ exercises/smelodesousa/F3/3-digits/descr.md | 16 ++ exercises/smelodesousa/F3/3-digits/meta.json | 9 + exercises/smelodesousa/F3/3-digits/prelude.ml | 0 exercises/smelodesousa/F3/3-digits/prepare.ml | 0 .../smelodesousa/F3/3-digits/solution.ml | 10 + .../smelodesousa/F3/3-digits/template.ml | 1 + exercises/smelodesousa/F3/3-digits/test.ml | 18 ++ .../F3/3-dragon-greatness/descr.md | 65 ++++++ .../F3/3-dragon-greatness/meta.json | 12 + .../F3/3-dragon-greatness/prelude.ml | 0 .../F3/3-dragon-greatness/prepare.ml | 0 .../F3/3-dragon-greatness/solution.ml | 106 +++++++++ .../F3/3-dragon-greatness/template.ml | 3 + .../F3/3-dragon-greatness/test.ml | 31 +++ .../F3/3-fast-exponentiation/descr.md | 45 ++++ .../F3/3-fast-exponentiation/meta.json | 9 + .../F3/3-fast-exponentiation/prelude.ml | 0 .../F3/3-fast-exponentiation/prepare.ml | 2 + .../F3/3-fast-exponentiation/solution.ml | 9 + .../F3/3-fast-exponentiation/template.ml | 3 + .../F3/3-fast-exponentiation/test.ml | 45 ++++ .../smelodesousa/F3/3-hofstadter/descr.md | 30 +++ .../smelodesousa/F3/3-hofstadter/meta.json | 9 + .../smelodesousa/F3/3-hofstadter/prelude.ml | 0 .../smelodesousa/F3/3-hofstadter/prepare.ml | 0 .../smelodesousa/F3/3-hofstadter/solution.ml | 7 + .../smelodesousa/F3/3-hofstadter/template.ml | 2 + .../smelodesousa/F3/3-hofstadter/test.ml | 20 ++ .../F3/3-how-many-mountains/N4.png | Bin 0 -> 49896 bytes .../F3/3-how-many-mountains/N43.png | Bin 0 -> 144124 bytes .../F3/3-how-many-mountains/descr.md | 42 ++++ .../F3/3-how-many-mountains/meta.json | 13 ++ .../F3/3-how-many-mountains/prelude.ml | 0 .../F3/3-how-many-mountains/prepare.ml | 0 .../F3/3-how-many-mountains/solution.ml | 22 ++ .../F3/3-how-many-mountains/template.ml | 1 + .../F3/3-how-many-mountains/test.ml | 19 ++ .../F3/3-manhattan-distance/descr.md | 13 ++ .../F3/3-manhattan-distance/meta.json | 9 + .../F3/3-manhattan-distance/prelude.ml | 0 .../F3/3-manhattan-distance/prepare.ml | 0 .../F3/3-manhattan-distance/solution.ml | 2 + .../F3/3-manhattan-distance/template.ml | 1 + .../F3/3-manhattan-distance/test.ml | 29 +++ .../F3/3-mccarthy-91-function/descr.md | 30 +++ .../F3/3-mccarthy-91-function/meta.json | 9 + .../F3/3-mccarthy-91-function/prelude.ml | 0 .../F3/3-mccarthy-91-function/prepare.ml | 0 .../F3/3-mccarthy-91-function/solution.ml | 8 + .../F3/3-mccarthy-91-function/template.ml | 5 + .../F3/3-mccarthy-91-function/test.ml | 37 ++++ exercises/smelodesousa/F3/3-pi/descr.md | 43 ++++ exercises/smelodesousa/F3/3-pi/meta.json | 12 + exercises/smelodesousa/F3/3-pi/prelude.ml | 0 exercises/smelodesousa/F3/3-pi/prepare.ml | 0 exercises/smelodesousa/F3/3-pi/solution.ml | 21 ++ exercises/smelodesousa/F3/3-pi/template.ml | 1 + exercises/smelodesousa/F3/3-pi/test.ml | 42 ++++ .../smelodesousa/F3/3-reach-infinity/descr.md | 37 ++++ .../F3/3-reach-infinity/meta.json | 9 + .../F3/3-reach-infinity/prelude.ml | 0 .../F3/3-reach-infinity/prepare.ml | 0 .../F3/3-reach-infinity/solution.ml | 10 + .../F3/3-reach-infinity/template.ml | 1 + .../smelodesousa/F3/3-reach-infinity/test.ml | 39 ++++ .../smelodesousa/F3/3-seq-hofstadter/descr.md | 32 +++ .../F3/3-seq-hofstadter/meta.json | 9 + .../F3/3-seq-hofstadter/prelude.ml | 0 .../F3/3-seq-hofstadter/prepare.ml | 0 .../F3/3-seq-hofstadter/solution.ml | 14 ++ .../F3/3-seq-hofstadter/template.ml | 1 + .../smelodesousa/F3/3-seq-hofstadter/test.ml | 25 +++ .../smelodesousa/F3/3-simple-math/descr.md | 22 ++ .../smelodesousa/F3/3-simple-math/meta.json | 12 + .../smelodesousa/F3/3-simple-math/prelude.ml | 3 + .../smelodesousa/F3/3-simple-math/prepare.ml | 0 .../smelodesousa/F3/3-simple-math/solution.ml | 10 + .../smelodesousa/F3/3-simple-math/template.ml | 3 + .../smelodesousa/F3/3-simple-math/test.ml | 67 ++++++ .../descr.md | 43 ++++ .../meta.json | 13 ++ .../prelude.ml | 0 .../prepare.ml | 0 .../solution.ml | 57 +++++ .../template.ml | 1 + .../test.ml | 16 ++ exercises/smelodesousa/F3/3-sums/descr.md | 21 ++ exercises/smelodesousa/F3/3-sums/meta.json | 9 + exercises/smelodesousa/F3/3-sums/prelude.ml | 0 exercises/smelodesousa/F3/3-sums/prepare.ml | 0 exercises/smelodesousa/F3/3-sums/solution.ml | 21 ++ exercises/smelodesousa/F3/3-sums/template.ml | 5 + exercises/smelodesousa/F3/3-sums/test.ml | 55 +++++ .../smelodesousa/F3/3-triangles/descr.md | 28 +++ .../smelodesousa/F3/3-triangles/meta.json | 9 + .../smelodesousa/F3/3-triangles/prelude.ml | 0 .../smelodesousa/F3/3-triangles/prepare.ml | 0 .../smelodesousa/F3/3-triangles/solution.ml | 4 + .../smelodesousa/F3/3-triangles/template.ml | 2 + exercises/smelodesousa/F3/3-triangles/test.ml | 19 ++ .../smelodesousa/F3/3-tribonacci/descr.md | 34 +++ .../smelodesousa/F3/3-tribonacci/meta.json | 12 + .../smelodesousa/F3/3-tribonacci/prelude.ml | 0 .../smelodesousa/F3/3-tribonacci/prepare.ml | 0 .../smelodesousa/F3/3-tribonacci/solution.ml | 51 +++++ .../smelodesousa/F3/3-tribonacci/template.ml | 3 + .../smelodesousa/F3/3-tribonacci/test.ml | 61 +++++ exercises/smelodesousa/F4/4-fun-1/descr.md | 16 ++ exercises/smelodesousa/F4/4-fun-1/meta.json | 9 + exercises/smelodesousa/F4/4-fun-1/prelude.ml | 0 exercises/smelodesousa/F4/4-fun-1/prepare.ml | 3 + exercises/smelodesousa/F4/4-fun-1/solution.ml | 21 ++ exercises/smelodesousa/F4/4-fun-1/template.ml | 21 ++ exercises/smelodesousa/F4/4-fun-1/test.ml | 208 ++++++++++++++++++ exercises/smelodesousa/F4/4-fun-2/descr.md | 47 ++++ exercises/smelodesousa/F4/4-fun-2/meta.json | 9 + exercises/smelodesousa/F4/4-fun-2/prelude.ml | 0 exercises/smelodesousa/F4/4-fun-2/prepare.ml | 6 + exercises/smelodesousa/F4/4-fun-2/solution.ml | 15 ++ exercises/smelodesousa/F4/4-fun-2/template.ml | 15 ++ exercises/smelodesousa/F4/4-fun-2/test.ml | 186 ++++++++++++++++ exercises/smelodesousa/F4/4-type-1/descr.md | 5 + exercises/smelodesousa/F4/4-type-1/meta.json | 9 + exercises/smelodesousa/F4/4-type-1/prelude.ml | 0 exercises/smelodesousa/F4/4-type-1/prepare.ml | 0 .../smelodesousa/F4/4-type-1/solution.ml | 5 + .../smelodesousa/F4/4-type-1/template.ml | 5 + exercises/smelodesousa/F4/4-type-1/test.ml | 80 +++++++ exercises/smelodesousa/F4/4-type-2/descr.md | 23 ++ exercises/smelodesousa/F4/4-type-2/meta.json | 9 + exercises/smelodesousa/F4/4-type-2/prelude.ml | 0 exercises/smelodesousa/F4/4-type-2/prepare.ml | 2 + .../smelodesousa/F4/4-type-2/solution.ml | 3 + .../smelodesousa/F4/4-type-2/template.ml | 3 + exercises/smelodesousa/F4/4-type-2/test.ml | 21 ++ exercises/smelodesousa/F4/4-type-3/descr.md | 21 ++ exercises/smelodesousa/F4/4-type-3/meta.json | 12 + exercises/smelodesousa/F4/4-type-3/prelude.ml | 0 exercises/smelodesousa/F4/4-type-3/prepare.ml | 1 + .../smelodesousa/F4/4-type-3/solution.ml | 4 + .../smelodesousa/F4/4-type-3/template.ml | 2 + exercises/smelodesousa/F4/4-type-3/test.ml | 16 ++ exercises/smelodesousa/F4/4-type-4/descr.md | 22 ++ exercises/smelodesousa/F4/4-type-4/meta.json | 12 + exercises/smelodesousa/F4/4-type-4/prelude.ml | 0 exercises/smelodesousa/F4/4-type-4/prepare.ml | 1 + .../smelodesousa/F4/4-type-4/solution.ml | 2 + .../smelodesousa/F4/4-type-4/template.ml | 2 + exercises/smelodesousa/F4/4-type-4/test.ml | 16 ++ .../F5/5-absolute-majority/descr.md | 70 ++++++ .../F5/5-absolute-majority/meta.json | 9 + .../F5/5-absolute-majority/prelude.ml | 0 .../F5/5-absolute-majority/prepare.ml | 0 .../F5/5-absolute-majority/solution.ml | 52 +++++ .../F5/5-absolute-majority/template.ml | 3 + .../F5/5-absolute-majority/test.ml | 35 +++ exercises/smelodesousa/F5/5-brackets/descr.md | 20 ++ .../smelodesousa/F5/5-brackets/meta.json | 9 + .../smelodesousa/F5/5-brackets/prelude.ml | 0 .../smelodesousa/F5/5-brackets/prepare.ml | 0 .../smelodesousa/F5/5-brackets/solution.ml | 12 + .../smelodesousa/F5/5-brackets/template.ml | 2 + exercises/smelodesousa/F5/5-brackets/test.ml | 26 +++ .../F5/5-burrows-wheeler/descr.md | 62 ++++++ .../F5/5-burrows-wheeler/meta.json | 9 + .../F5/5-burrows-wheeler/prelude.ml | 0 .../F5/5-burrows-wheeler/prepare.ml | 0 .../F5/5-burrows-wheeler/solution.ml | 124 +++++++++++ .../F5/5-burrows-wheeler/template.ml | 2 + .../smelodesousa/F5/5-burrows-wheeler/test.ml | 34 +++ .../smelodesousa/F5/5-gray-codes/descr.md | 61 +++++ .../smelodesousa/F5/5-gray-codes/meta.json | 9 + .../smelodesousa/F5/5-gray-codes/prelude.ml | 0 .../smelodesousa/F5/5-gray-codes/prepare.ml | 0 .../smelodesousa/F5/5-gray-codes/solution.ml | 56 +++++ .../smelodesousa/F5/5-gray-codes/template.ml | 7 + .../smelodesousa/F5/5-gray-codes/test.ml | 46 ++++ exercises/smelodesousa/F5/5-half/descr.md | 7 + exercises/smelodesousa/F5/5-half/meta.json | 9 + exercises/smelodesousa/F5/5-half/prelude.ml | 0 exercises/smelodesousa/F5/5-half/prepare.ml | 0 exercises/smelodesousa/F5/5-half/solution.ml | 8 + exercises/smelodesousa/F5/5-half/template.ml | 2 + exercises/smelodesousa/F5/5-half/test.ml | 15 ++ exercises/smelodesousa/F5/5-holand/descr.md | 58 +++++ exercises/smelodesousa/F5/5-holand/meta.json | 9 + exercises/smelodesousa/F5/5-holand/prelude.ml | 5 + exercises/smelodesousa/F5/5-holand/prepare.ml | 0 .../smelodesousa/F5/5-holand/solution.ml | 21 ++ .../smelodesousa/F5/5-holand/template.ml | 1 + exercises/smelodesousa/F5/5-holand/test.ml | 44 ++++ exercises/smelodesousa/F5/5-horner/descr.md | 46 ++++ exercises/smelodesousa/F5/5-horner/meta.json | 9 + exercises/smelodesousa/F5/5-horner/prelude.ml | 0 exercises/smelodesousa/F5/5-horner/prepare.ml | 4 + .../smelodesousa/F5/5-horner/solution.ml | 61 +++++ .../smelodesousa/F5/5-horner/template.ml | 4 + exercises/smelodesousa/F5/5-horner/test.ml | 82 +++++++ .../F5/5-lists-sublists-1/descr.md | 9 + .../F5/5-lists-sublists-1/meta.json | 9 + .../F5/5-lists-sublists-1/prelude.ml | 0 .../F5/5-lists-sublists-1/prepare.ml | 1 + .../F5/5-lists-sublists-1/solution.ml | 10 + .../F5/5-lists-sublists-1/template.ml | 7 + .../F5/5-lists-sublists-1/test.ml | 41 ++++ .../F5/5-lists-sublists-2/descr.md | 14 ++ .../F5/5-lists-sublists-2/meta.json | 9 + .../F5/5-lists-sublists-2/prelude.ml | 0 .../F5/5-lists-sublists-2/prepare.ml | 1 + .../F5/5-lists-sublists-2/solution.ml | 9 + .../F5/5-lists-sublists-2/template.ml | 8 + .../F5/5-lists-sublists-2/test.ml | 41 ++++ .../F5/5-lists-sublists-3/descr.md | 7 + .../F5/5-lists-sublists-3/meta.json | 9 + .../F5/5-lists-sublists-3/prelude.ml | 0 .../F5/5-lists-sublists-3/prepare.ml | 1 + .../F5/5-lists-sublists-3/solution.ml | 15 ++ .../F5/5-lists-sublists-3/template.ml | 8 + .../F5/5-lists-sublists-3/test.ml | 41 ++++ .../F5/5-lists-sublists-4/descr.md | 22 ++ .../F5/5-lists-sublists-4/meta.json | 9 + .../F5/5-lists-sublists-4/prelude.ml | 0 .../F5/5-lists-sublists-4/prepare.ml | 0 .../F5/5-lists-sublists-4/solution.ml | 18 ++ .../F5/5-lists-sublists-4/template.ml | 2 + .../F5/5-lists-sublists-4/test.ml | 15 ++ exercises/smelodesousa/F5/5-lists/descr.md | 23 ++ exercises/smelodesousa/F5/5-lists/meta.json | 9 + exercises/smelodesousa/F5/5-lists/prelude.ml | 0 exercises/smelodesousa/F5/5-lists/prepare.ml | 0 exercises/smelodesousa/F5/5-lists/solution.ml | 29 +++ exercises/smelodesousa/F5/5-lists/template.ml | 20 ++ exercises/smelodesousa/F5/5-lists/test.ml | 136 ++++++++++++ exercises/smelodesousa/F5/5-lotto/descr.md | 27 +++ exercises/smelodesousa/F5/5-lotto/meta.json | 9 + exercises/smelodesousa/F5/5-lotto/prelude.ml | 0 exercises/smelodesousa/F5/5-lotto/prepare.ml | 0 exercises/smelodesousa/F5/5-lotto/solution.ml | 29 +++ exercises/smelodesousa/F5/5-lotto/template.ml | 9 + exercises/smelodesousa/F5/5-lotto/test.ml | 123 +++++++++++ .../smelodesousa/F5/5-max-sub-list/descr.md | 32 +++ .../smelodesousa/F5/5-max-sub-list/meta.json | 9 + .../smelodesousa/F5/5-max-sub-list/prelude.ml | 0 .../smelodesousa/F5/5-max-sub-list/prepare.ml | 0 .../F5/5-max-sub-list/solution.ml | 66 ++++++ .../F5/5-max-sub-list/template.ml | 2 + .../smelodesousa/F5/5-max-sub-list/test.ml | 39 ++++ exercises/smelodesousa/F5/5-pizzaria/descr.md | 53 +++++ .../smelodesousa/F5/5-pizzaria/meta.json | 9 + .../smelodesousa/F5/5-pizzaria/prelude.ml | 0 .../smelodesousa/F5/5-pizzaria/prepare.ml | 0 .../smelodesousa/F5/5-pizzaria/solution.ml | 22 ++ .../smelodesousa/F5/5-pizzaria/template.ml | 1 + exercises/smelodesousa/F5/5-pizzaria/test.ml | 24 ++ .../F5/5-randomness-is-hard/descr.md | 46 ++++ .../F5/5-randomness-is-hard/meta.json | 9 + .../F5/5-randomness-is-hard/prelude.ml | 0 .../F5/5-randomness-is-hard/prepare.ml | 0 .../F5/5-randomness-is-hard/solution.ml | 21 ++ .../F5/5-randomness-is-hard/template.ml | 1 + .../F5/5-randomness-is-hard/test.ml | 18 ++ exercises/smelodesousa/F5/5-rle/descr.md | 27 +++ exercises/smelodesousa/F5/5-rle/meta.json | 9 + exercises/smelodesousa/F5/5-rle/prelude.ml | 0 exercises/smelodesousa/F5/5-rle/prepare.ml | 6 + exercises/smelodesousa/F5/5-rle/solution.ml | 29 +++ exercises/smelodesousa/F5/5-rle/template.ml | 7 + exercises/smelodesousa/F5/5-rle/test.ml | 64 ++++++ exercises/smelodesousa/F5/5-salc/descr.md | 28 +++ exercises/smelodesousa/F5/5-salc/meta.json | 9 + exercises/smelodesousa/F5/5-salc/prelude.ml | 0 exercises/smelodesousa/F5/5-salc/prepare.ml | 0 exercises/smelodesousa/F5/5-salc/solution.ml | 117 ++++++++++ exercises/smelodesousa/F5/5-salc/template.ml | 4 + exercises/smelodesousa/F5/5-salc/test.ml | 53 +++++ exercises/smelodesousa/F5/5-seq-true/descr.md | 16 ++ .../smelodesousa/F5/5-seq-true/meta.json | 9 + .../smelodesousa/F5/5-seq-true/prelude.ml | 0 .../smelodesousa/F5/5-seq-true/prepare.ml | 0 .../smelodesousa/F5/5-seq-true/solution.ml | 19 ++ .../smelodesousa/F5/5-seq-true/template.ml | 1 + exercises/smelodesousa/F5/5-seq-true/test.ml | 33 +++ .../F5/5-shine-in-society/descr.md | 55 +++++ .../F5/5-shine-in-society/meta.json | 9 + .../F5/5-shine-in-society/prelude.ml | 35 +++ .../F5/5-shine-in-society/prepare.ml | 0 .../F5/5-shine-in-society/solution.ml | 2 + .../F5/5-shine-in-society/template.ml | 1 + .../F5/5-shine-in-society/test.ml | 21 ++ .../F5/5-subsequence-of-lists/descr.md | 15 ++ .../F5/5-subsequence-of-lists/meta.json | 9 + .../F5/5-subsequence-of-lists/prelude.ml | 0 .../F5/5-subsequence-of-lists/prepare.ml | 0 .../F5/5-subsequence-of-lists/solution.ml | 19 ++ .../F5/5-subsequence-of-lists/template.ml | 1 + .../F5/5-subsequence-of-lists/test.ml | 41 ++++ .../smelodesousa/F5/5-zombie-attack/descr.md | 51 +++++ .../smelodesousa/F5/5-zombie-attack/meta.json | 9 + .../F5/5-zombie-attack/prelude.ml | 0 .../F5/5-zombie-attack/prepare.ml | 0 .../F5/5-zombie-attack/solution.ml | 47 ++++ .../F5/5-zombie-attack/template.ml | 1 + .../smelodesousa/F5/5-zombie-attack/test.ml | 55 +++++ .../F5/5-zombie-attack/zombie-0.png | Bin 0 -> 8514 bytes .../F5/5-zombie-attack/zombie-11.png | Bin 0 -> 8872 bytes 417 files changed, 6964 insertions(+) create mode 100644 exercises/smelodesousa/F1/1-errors/descr.md create mode 100644 exercises/smelodesousa/F1/1-errors/meta.json create mode 100644 exercises/smelodesousa/F1/1-errors/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-errors/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-errors/solution.ml create mode 100644 exercises/smelodesousa/F1/1-errors/template.ml create mode 100644 exercises/smelodesousa/F1/1-errors/test.ml create mode 100644 exercises/smelodesousa/F1/1-fun-calls/descr.md create mode 100644 exercises/smelodesousa/F1/1-fun-calls/meta.json create mode 100644 exercises/smelodesousa/F1/1-fun-calls/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-fun-calls/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-fun-calls/solution.ml create mode 100644 exercises/smelodesousa/F1/1-fun-calls/template.ml create mode 100644 exercises/smelodesousa/F1/1-fun-calls/test.ml create mode 100644 exercises/smelodesousa/F1/1-future/descr.md create mode 100644 exercises/smelodesousa/F1/1-future/meta.json create mode 100644 exercises/smelodesousa/F1/1-future/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-future/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-future/solution.ml create mode 100644 exercises/smelodesousa/F1/1-future/template.ml create mode 100644 exercises/smelodesousa/F1/1-future/test.ml create mode 100644 exercises/smelodesousa/F1/1-mistery/descr.md create mode 100644 exercises/smelodesousa/F1/1-mistery/meta.json create mode 100644 exercises/smelodesousa/F1/1-mistery/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-mistery/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-mistery/solution.ml create mode 100644 exercises/smelodesousa/F1/1-mistery/template.ml create mode 100644 exercises/smelodesousa/F1/1-mistery/test.ml create mode 100644 exercises/smelodesousa/F1/1-true-false/descr.md create mode 100644 exercises/smelodesousa/F1/1-true-false/meta.json create mode 100644 exercises/smelodesousa/F1/1-true-false/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-true-false/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-true-false/solution.ml create mode 100644 exercises/smelodesousa/F1/1-true-false/template.ml create mode 100644 exercises/smelodesousa/F1/1-true-false/test.ml create mode 100644 exercises/smelodesousa/F1/1-tuples/descr.md create mode 100644 exercises/smelodesousa/F1/1-tuples/meta.json create mode 100644 exercises/smelodesousa/F1/1-tuples/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-tuples/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-tuples/solution.ml create mode 100644 exercises/smelodesousa/F1/1-tuples/template.ml create mode 100644 exercises/smelodesousa/F1/1-tuples/test.ml create mode 100644 exercises/smelodesousa/F1/1-type-error/descr.md create mode 100644 exercises/smelodesousa/F1/1-type-error/meta.json create mode 100644 exercises/smelodesousa/F1/1-type-error/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-type-error/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-type-error/solution.ml create mode 100644 exercises/smelodesousa/F1/1-type-error/template.ml create mode 100644 exercises/smelodesousa/F1/1-type-error/test.ml create mode 100644 exercises/smelodesousa/F1/1-type-value/descr.md create mode 100644 exercises/smelodesousa/F1/1-type-value/meta.json create mode 100644 exercises/smelodesousa/F1/1-type-value/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-type-value/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-type-value/solution.ml create mode 100644 exercises/smelodesousa/F1/1-type-value/template.ml create mode 100644 exercises/smelodesousa/F1/1-type-value/test.ml create mode 100644 exercises/smelodesousa/F1/1-type/descr.md create mode 100644 exercises/smelodesousa/F1/1-type/meta.json create mode 100644 exercises/smelodesousa/F1/1-type/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-type/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-type/solution.ml create mode 100644 exercises/smelodesousa/F1/1-type/template.ml create mode 100644 exercises/smelodesousa/F1/1-type/test.ml create mode 100644 exercises/smelodesousa/F1/1-type2/descr.md create mode 100644 exercises/smelodesousa/F1/1-type2/meta.json create mode 100644 exercises/smelodesousa/F1/1-type2/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-type2/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-type2/solution.ml create mode 100644 exercises/smelodesousa/F1/1-type2/template.ml create mode 100644 exercises/smelodesousa/F1/1-type2/test.ml create mode 100644 exercises/smelodesousa/F1/1-what-type/descr.md create mode 100644 exercises/smelodesousa/F1/1-what-type/meta.json create mode 100644 exercises/smelodesousa/F1/1-what-type/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-what-type/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-what-type/solution.ml create mode 100644 exercises/smelodesousa/F1/1-what-type/template.ml create mode 100644 exercises/smelodesousa/F1/1-what-type/test.ml create mode 100644 exercises/smelodesousa/F1/1-what-type2/descr.md create mode 100644 exercises/smelodesousa/F1/1-what-type2/meta.json create mode 100644 exercises/smelodesousa/F1/1-what-type2/prelude.ml create mode 100644 exercises/smelodesousa/F1/1-what-type2/prepare.ml create mode 100644 exercises/smelodesousa/F1/1-what-type2/solution.ml create mode 100644 exercises/smelodesousa/F1/1-what-type2/template.ml create mode 100644 exercises/smelodesousa/F1/1-what-type2/test.ml create mode 100644 exercises/smelodesousa/F3/3-a-historic-algorithm/descr.md create mode 100644 exercises/smelodesousa/F3/3-a-historic-algorithm/meta.json create mode 100644 exercises/smelodesousa/F3/3-a-historic-algorithm/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-a-historic-algorithm/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-a-historic-algorithm/solution.ml create mode 100644 exercises/smelodesousa/F3/3-a-historic-algorithm/template.ml create mode 100644 exercises/smelodesousa/F3/3-a-historic-algorithm/test.ml create mode 100644 exercises/smelodesousa/F3/3-catalan/descr.md create mode 100644 exercises/smelodesousa/F3/3-catalan/meta.json create mode 100644 exercises/smelodesousa/F3/3-catalan/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-catalan/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-catalan/solution.ml create mode 100644 exercises/smelodesousa/F3/3-catalan/template.ml create mode 100644 exercises/smelodesousa/F3/3-catalan/test.ml create mode 100644 exercises/smelodesousa/F3/3-collatz-hailstones/descr.md create mode 100644 exercises/smelodesousa/F3/3-collatz-hailstones/meta.json create mode 100644 exercises/smelodesousa/F3/3-collatz-hailstones/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-collatz-hailstones/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-collatz-hailstones/solution.ml create mode 100644 exercises/smelodesousa/F3/3-collatz-hailstones/template.ml create mode 100644 exercises/smelodesousa/F3/3-collatz-hailstones/test.ml create mode 100644 exercises/smelodesousa/F3/3-dichotomy/descr.md create mode 100644 exercises/smelodesousa/F3/3-dichotomy/meta.json create mode 100644 exercises/smelodesousa/F3/3-dichotomy/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-dichotomy/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-dichotomy/solution.ml create mode 100644 exercises/smelodesousa/F3/3-dichotomy/template.ml create mode 100644 exercises/smelodesousa/F3/3-dichotomy/test.ml create mode 100644 exercises/smelodesousa/F3/3-digits/descr.md create mode 100644 exercises/smelodesousa/F3/3-digits/meta.json create mode 100644 exercises/smelodesousa/F3/3-digits/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-digits/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-digits/solution.ml create mode 100644 exercises/smelodesousa/F3/3-digits/template.ml create mode 100644 exercises/smelodesousa/F3/3-digits/test.ml create mode 100644 exercises/smelodesousa/F3/3-dragon-greatness/descr.md create mode 100644 exercises/smelodesousa/F3/3-dragon-greatness/meta.json create mode 100644 exercises/smelodesousa/F3/3-dragon-greatness/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-dragon-greatness/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-dragon-greatness/solution.ml create mode 100644 exercises/smelodesousa/F3/3-dragon-greatness/template.ml create mode 100644 exercises/smelodesousa/F3/3-dragon-greatness/test.ml create mode 100644 exercises/smelodesousa/F3/3-fast-exponentiation/descr.md create mode 100644 exercises/smelodesousa/F3/3-fast-exponentiation/meta.json create mode 100644 exercises/smelodesousa/F3/3-fast-exponentiation/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-fast-exponentiation/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-fast-exponentiation/solution.ml create mode 100644 exercises/smelodesousa/F3/3-fast-exponentiation/template.ml create mode 100644 exercises/smelodesousa/F3/3-fast-exponentiation/test.ml create mode 100644 exercises/smelodesousa/F3/3-hofstadter/descr.md create mode 100644 exercises/smelodesousa/F3/3-hofstadter/meta.json create mode 100644 exercises/smelodesousa/F3/3-hofstadter/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-hofstadter/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-hofstadter/solution.ml create mode 100644 exercises/smelodesousa/F3/3-hofstadter/template.ml create mode 100644 exercises/smelodesousa/F3/3-hofstadter/test.ml create mode 100644 exercises/smelodesousa/F3/3-how-many-mountains/N4.png create mode 100644 exercises/smelodesousa/F3/3-how-many-mountains/N43.png create mode 100644 exercises/smelodesousa/F3/3-how-many-mountains/descr.md create mode 100644 exercises/smelodesousa/F3/3-how-many-mountains/meta.json create mode 100644 exercises/smelodesousa/F3/3-how-many-mountains/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-how-many-mountains/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-how-many-mountains/solution.ml create mode 100644 exercises/smelodesousa/F3/3-how-many-mountains/template.ml create mode 100644 exercises/smelodesousa/F3/3-how-many-mountains/test.ml create mode 100644 exercises/smelodesousa/F3/3-manhattan-distance/descr.md create mode 100644 exercises/smelodesousa/F3/3-manhattan-distance/meta.json create mode 100644 exercises/smelodesousa/F3/3-manhattan-distance/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-manhattan-distance/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-manhattan-distance/solution.ml create mode 100644 exercises/smelodesousa/F3/3-manhattan-distance/template.ml create mode 100644 exercises/smelodesousa/F3/3-manhattan-distance/test.ml create mode 100644 exercises/smelodesousa/F3/3-mccarthy-91-function/descr.md create mode 100644 exercises/smelodesousa/F3/3-mccarthy-91-function/meta.json create mode 100644 exercises/smelodesousa/F3/3-mccarthy-91-function/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-mccarthy-91-function/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-mccarthy-91-function/solution.ml create mode 100644 exercises/smelodesousa/F3/3-mccarthy-91-function/template.ml create mode 100644 exercises/smelodesousa/F3/3-mccarthy-91-function/test.ml create mode 100644 exercises/smelodesousa/F3/3-pi/descr.md create mode 100644 exercises/smelodesousa/F3/3-pi/meta.json create mode 100644 exercises/smelodesousa/F3/3-pi/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-pi/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-pi/solution.ml create mode 100644 exercises/smelodesousa/F3/3-pi/template.ml create mode 100644 exercises/smelodesousa/F3/3-pi/test.ml create mode 100644 exercises/smelodesousa/F3/3-reach-infinity/descr.md create mode 100644 exercises/smelodesousa/F3/3-reach-infinity/meta.json create mode 100644 exercises/smelodesousa/F3/3-reach-infinity/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-reach-infinity/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-reach-infinity/solution.ml create mode 100644 exercises/smelodesousa/F3/3-reach-infinity/template.ml create mode 100644 exercises/smelodesousa/F3/3-reach-infinity/test.ml create mode 100644 exercises/smelodesousa/F3/3-seq-hofstadter/descr.md create mode 100644 exercises/smelodesousa/F3/3-seq-hofstadter/meta.json create mode 100644 exercises/smelodesousa/F3/3-seq-hofstadter/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-seq-hofstadter/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-seq-hofstadter/solution.ml create mode 100644 exercises/smelodesousa/F3/3-seq-hofstadter/template.ml create mode 100644 exercises/smelodesousa/F3/3-seq-hofstadter/test.ml create mode 100644 exercises/smelodesousa/F3/3-simple-math/descr.md create mode 100644 exercises/smelodesousa/F3/3-simple-math/meta.json create mode 100644 exercises/smelodesousa/F3/3-simple-math/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-simple-math/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-simple-math/solution.ml create mode 100644 exercises/smelodesousa/F3/3-simple-math/template.ml create mode 100644 exercises/smelodesousa/F3/3-simple-math/test.ml create mode 100644 exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/descr.md create mode 100644 exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/meta.json create mode 100644 exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/solution.ml create mode 100644 exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/template.ml create mode 100644 exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/test.ml create mode 100644 exercises/smelodesousa/F3/3-sums/descr.md create mode 100644 exercises/smelodesousa/F3/3-sums/meta.json create mode 100644 exercises/smelodesousa/F3/3-sums/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-sums/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-sums/solution.ml create mode 100644 exercises/smelodesousa/F3/3-sums/template.ml create mode 100644 exercises/smelodesousa/F3/3-sums/test.ml create mode 100644 exercises/smelodesousa/F3/3-triangles/descr.md create mode 100644 exercises/smelodesousa/F3/3-triangles/meta.json create mode 100644 exercises/smelodesousa/F3/3-triangles/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-triangles/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-triangles/solution.ml create mode 100644 exercises/smelodesousa/F3/3-triangles/template.ml create mode 100644 exercises/smelodesousa/F3/3-triangles/test.ml create mode 100644 exercises/smelodesousa/F3/3-tribonacci/descr.md create mode 100644 exercises/smelodesousa/F3/3-tribonacci/meta.json create mode 100644 exercises/smelodesousa/F3/3-tribonacci/prelude.ml create mode 100644 exercises/smelodesousa/F3/3-tribonacci/prepare.ml create mode 100644 exercises/smelodesousa/F3/3-tribonacci/solution.ml create mode 100644 exercises/smelodesousa/F3/3-tribonacci/template.ml create mode 100644 exercises/smelodesousa/F3/3-tribonacci/test.ml create mode 100644 exercises/smelodesousa/F4/4-fun-1/descr.md create mode 100644 exercises/smelodesousa/F4/4-fun-1/meta.json create mode 100644 exercises/smelodesousa/F4/4-fun-1/prelude.ml create mode 100644 exercises/smelodesousa/F4/4-fun-1/prepare.ml create mode 100644 exercises/smelodesousa/F4/4-fun-1/solution.ml create mode 100644 exercises/smelodesousa/F4/4-fun-1/template.ml create mode 100644 exercises/smelodesousa/F4/4-fun-1/test.ml create mode 100644 exercises/smelodesousa/F4/4-fun-2/descr.md create mode 100644 exercises/smelodesousa/F4/4-fun-2/meta.json create mode 100644 exercises/smelodesousa/F4/4-fun-2/prelude.ml create mode 100644 exercises/smelodesousa/F4/4-fun-2/prepare.ml create mode 100644 exercises/smelodesousa/F4/4-fun-2/solution.ml create mode 100644 exercises/smelodesousa/F4/4-fun-2/template.ml create mode 100644 exercises/smelodesousa/F4/4-fun-2/test.ml create mode 100644 exercises/smelodesousa/F4/4-type-1/descr.md create mode 100644 exercises/smelodesousa/F4/4-type-1/meta.json create mode 100644 exercises/smelodesousa/F4/4-type-1/prelude.ml create mode 100644 exercises/smelodesousa/F4/4-type-1/prepare.ml create mode 100644 exercises/smelodesousa/F4/4-type-1/solution.ml create mode 100644 exercises/smelodesousa/F4/4-type-1/template.ml create mode 100644 exercises/smelodesousa/F4/4-type-1/test.ml create mode 100644 exercises/smelodesousa/F4/4-type-2/descr.md create mode 100644 exercises/smelodesousa/F4/4-type-2/meta.json create mode 100644 exercises/smelodesousa/F4/4-type-2/prelude.ml create mode 100644 exercises/smelodesousa/F4/4-type-2/prepare.ml create mode 100644 exercises/smelodesousa/F4/4-type-2/solution.ml create mode 100644 exercises/smelodesousa/F4/4-type-2/template.ml create mode 100644 exercises/smelodesousa/F4/4-type-2/test.ml create mode 100644 exercises/smelodesousa/F4/4-type-3/descr.md create mode 100644 exercises/smelodesousa/F4/4-type-3/meta.json create mode 100644 exercises/smelodesousa/F4/4-type-3/prelude.ml create mode 100644 exercises/smelodesousa/F4/4-type-3/prepare.ml create mode 100644 exercises/smelodesousa/F4/4-type-3/solution.ml create mode 100644 exercises/smelodesousa/F4/4-type-3/template.ml create mode 100644 exercises/smelodesousa/F4/4-type-3/test.ml create mode 100644 exercises/smelodesousa/F4/4-type-4/descr.md create mode 100644 exercises/smelodesousa/F4/4-type-4/meta.json create mode 100644 exercises/smelodesousa/F4/4-type-4/prelude.ml create mode 100644 exercises/smelodesousa/F4/4-type-4/prepare.ml create mode 100644 exercises/smelodesousa/F4/4-type-4/solution.ml create mode 100644 exercises/smelodesousa/F4/4-type-4/template.ml create mode 100644 exercises/smelodesousa/F4/4-type-4/test.ml create mode 100644 exercises/smelodesousa/F5/5-absolute-majority/descr.md create mode 100644 exercises/smelodesousa/F5/5-absolute-majority/meta.json create mode 100644 exercises/smelodesousa/F5/5-absolute-majority/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-absolute-majority/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-absolute-majority/solution.ml create mode 100644 exercises/smelodesousa/F5/5-absolute-majority/template.ml create mode 100644 exercises/smelodesousa/F5/5-absolute-majority/test.ml create mode 100644 exercises/smelodesousa/F5/5-brackets/descr.md create mode 100644 exercises/smelodesousa/F5/5-brackets/meta.json create mode 100644 exercises/smelodesousa/F5/5-brackets/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-brackets/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-brackets/solution.ml create mode 100644 exercises/smelodesousa/F5/5-brackets/template.ml create mode 100644 exercises/smelodesousa/F5/5-brackets/test.ml create mode 100644 exercises/smelodesousa/F5/5-burrows-wheeler/descr.md create mode 100644 exercises/smelodesousa/F5/5-burrows-wheeler/meta.json create mode 100644 exercises/smelodesousa/F5/5-burrows-wheeler/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-burrows-wheeler/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-burrows-wheeler/solution.ml create mode 100644 exercises/smelodesousa/F5/5-burrows-wheeler/template.ml create mode 100644 exercises/smelodesousa/F5/5-burrows-wheeler/test.ml create mode 100644 exercises/smelodesousa/F5/5-gray-codes/descr.md create mode 100644 exercises/smelodesousa/F5/5-gray-codes/meta.json create mode 100644 exercises/smelodesousa/F5/5-gray-codes/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-gray-codes/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-gray-codes/solution.ml create mode 100644 exercises/smelodesousa/F5/5-gray-codes/template.ml create mode 100644 exercises/smelodesousa/F5/5-gray-codes/test.ml create mode 100644 exercises/smelodesousa/F5/5-half/descr.md create mode 100644 exercises/smelodesousa/F5/5-half/meta.json create mode 100644 exercises/smelodesousa/F5/5-half/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-half/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-half/solution.ml create mode 100644 exercises/smelodesousa/F5/5-half/template.ml create mode 100644 exercises/smelodesousa/F5/5-half/test.ml create mode 100644 exercises/smelodesousa/F5/5-holand/descr.md create mode 100644 exercises/smelodesousa/F5/5-holand/meta.json create mode 100644 exercises/smelodesousa/F5/5-holand/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-holand/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-holand/solution.ml create mode 100644 exercises/smelodesousa/F5/5-holand/template.ml create mode 100644 exercises/smelodesousa/F5/5-holand/test.ml create mode 100644 exercises/smelodesousa/F5/5-horner/descr.md create mode 100644 exercises/smelodesousa/F5/5-horner/meta.json create mode 100644 exercises/smelodesousa/F5/5-horner/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-horner/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-horner/solution.ml create mode 100644 exercises/smelodesousa/F5/5-horner/template.ml create mode 100644 exercises/smelodesousa/F5/5-horner/test.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-1/descr.md create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-1/meta.json create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-1/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-1/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-1/solution.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-1/template.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-1/test.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-2/descr.md create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-2/meta.json create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-2/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-2/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-2/solution.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-2/template.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-2/test.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-3/descr.md create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-3/meta.json create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-3/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-3/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-3/solution.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-3/template.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-3/test.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-4/descr.md create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-4/meta.json create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-4/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-4/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-4/solution.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-4/template.ml create mode 100644 exercises/smelodesousa/F5/5-lists-sublists-4/test.ml create mode 100644 exercises/smelodesousa/F5/5-lists/descr.md create mode 100644 exercises/smelodesousa/F5/5-lists/meta.json create mode 100644 exercises/smelodesousa/F5/5-lists/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-lists/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-lists/solution.ml create mode 100644 exercises/smelodesousa/F5/5-lists/template.ml create mode 100644 exercises/smelodesousa/F5/5-lists/test.ml create mode 100644 exercises/smelodesousa/F5/5-lotto/descr.md create mode 100644 exercises/smelodesousa/F5/5-lotto/meta.json create mode 100644 exercises/smelodesousa/F5/5-lotto/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-lotto/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-lotto/solution.ml create mode 100644 exercises/smelodesousa/F5/5-lotto/template.ml create mode 100644 exercises/smelodesousa/F5/5-lotto/test.ml create mode 100644 exercises/smelodesousa/F5/5-max-sub-list/descr.md create mode 100644 exercises/smelodesousa/F5/5-max-sub-list/meta.json create mode 100644 exercises/smelodesousa/F5/5-max-sub-list/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-max-sub-list/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-max-sub-list/solution.ml create mode 100644 exercises/smelodesousa/F5/5-max-sub-list/template.ml create mode 100644 exercises/smelodesousa/F5/5-max-sub-list/test.ml create mode 100644 exercises/smelodesousa/F5/5-pizzaria/descr.md create mode 100644 exercises/smelodesousa/F5/5-pizzaria/meta.json create mode 100644 exercises/smelodesousa/F5/5-pizzaria/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-pizzaria/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-pizzaria/solution.ml create mode 100644 exercises/smelodesousa/F5/5-pizzaria/template.ml create mode 100644 exercises/smelodesousa/F5/5-pizzaria/test.ml create mode 100644 exercises/smelodesousa/F5/5-randomness-is-hard/descr.md create mode 100644 exercises/smelodesousa/F5/5-randomness-is-hard/meta.json create mode 100644 exercises/smelodesousa/F5/5-randomness-is-hard/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-randomness-is-hard/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-randomness-is-hard/solution.ml create mode 100644 exercises/smelodesousa/F5/5-randomness-is-hard/template.ml create mode 100644 exercises/smelodesousa/F5/5-randomness-is-hard/test.ml create mode 100644 exercises/smelodesousa/F5/5-rle/descr.md create mode 100644 exercises/smelodesousa/F5/5-rle/meta.json create mode 100644 exercises/smelodesousa/F5/5-rle/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-rle/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-rle/solution.ml create mode 100644 exercises/smelodesousa/F5/5-rle/template.ml create mode 100644 exercises/smelodesousa/F5/5-rle/test.ml create mode 100644 exercises/smelodesousa/F5/5-salc/descr.md create mode 100644 exercises/smelodesousa/F5/5-salc/meta.json create mode 100644 exercises/smelodesousa/F5/5-salc/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-salc/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-salc/solution.ml create mode 100644 exercises/smelodesousa/F5/5-salc/template.ml create mode 100644 exercises/smelodesousa/F5/5-salc/test.ml create mode 100644 exercises/smelodesousa/F5/5-seq-true/descr.md create mode 100644 exercises/smelodesousa/F5/5-seq-true/meta.json create mode 100644 exercises/smelodesousa/F5/5-seq-true/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-seq-true/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-seq-true/solution.ml create mode 100644 exercises/smelodesousa/F5/5-seq-true/template.ml create mode 100644 exercises/smelodesousa/F5/5-seq-true/test.ml create mode 100644 exercises/smelodesousa/F5/5-shine-in-society/descr.md create mode 100644 exercises/smelodesousa/F5/5-shine-in-society/meta.json create mode 100644 exercises/smelodesousa/F5/5-shine-in-society/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-shine-in-society/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-shine-in-society/solution.ml create mode 100644 exercises/smelodesousa/F5/5-shine-in-society/template.ml create mode 100644 exercises/smelodesousa/F5/5-shine-in-society/test.ml create mode 100644 exercises/smelodesousa/F5/5-subsequence-of-lists/descr.md create mode 100644 exercises/smelodesousa/F5/5-subsequence-of-lists/meta.json create mode 100644 exercises/smelodesousa/F5/5-subsequence-of-lists/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-subsequence-of-lists/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-subsequence-of-lists/solution.ml create mode 100644 exercises/smelodesousa/F5/5-subsequence-of-lists/template.ml create mode 100644 exercises/smelodesousa/F5/5-subsequence-of-lists/test.ml create mode 100644 exercises/smelodesousa/F5/5-zombie-attack/descr.md create mode 100644 exercises/smelodesousa/F5/5-zombie-attack/meta.json create mode 100644 exercises/smelodesousa/F5/5-zombie-attack/prelude.ml create mode 100644 exercises/smelodesousa/F5/5-zombie-attack/prepare.ml create mode 100644 exercises/smelodesousa/F5/5-zombie-attack/solution.ml create mode 100644 exercises/smelodesousa/F5/5-zombie-attack/template.ml create mode 100644 exercises/smelodesousa/F5/5-zombie-attack/test.ml create mode 100644 exercises/smelodesousa/F5/5-zombie-attack/zombie-0.png create mode 100644 exercises/smelodesousa/F5/5-zombie-attack/zombie-11.png diff --git a/exercises/smelodesousa/F1/1-errors/descr.md b/exercises/smelodesousa/F1/1-errors/descr.md new file mode 100644 index 0000000..8510598 --- /dev/null +++ b/exercises/smelodesousa/F1/1-errors/descr.md @@ -0,0 +1,6 @@ +Evaluate the following expressions, observe the resultant error messages and propose a correction, i.e., a correct expression that solves the detected problems. + +- `let quarter_pi = 3.14 / 4.` +- `let in_order = 1 < 2 < 3` +- `let positive = let a = 42 in if a >= 0 then true` +- `let double_positive = let x = cos 5. in (if (x < 0) then x else -x) *. 2` diff --git a/exercises/smelodesousa/F1/1-errors/meta.json b/exercises/smelodesousa/F1/1-errors/meta.json new file mode 100644 index 0000000..ca18826 --- /dev/null +++ b/exercises/smelodesousa/F1/1-errors/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Errors", + "identifier" : "1.5", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F1/1-future"] +} diff --git a/exercises/smelodesousa/F1/1-errors/prelude.ml b/exercises/smelodesousa/F1/1-errors/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-errors/prepare.ml b/exercises/smelodesousa/F1/1-errors/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-errors/solution.ml b/exercises/smelodesousa/F1/1-errors/solution.ml new file mode 100644 index 0000000..907b68d --- /dev/null +++ b/exercises/smelodesousa/F1/1-errors/solution.ml @@ -0,0 +1,7 @@ +let quarter_pi = 3.14 /. 4. + +let in_order = (1 < 2) && (2 < 3) + +let positive = let a = 42 in a >= 0 + +let double_positive = let x = cos 5. in (if (x < 0.0) then x else -.x) *. 2. \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-errors/template.ml b/exercises/smelodesousa/F1/1-errors/template.ml new file mode 100644 index 0000000..cbf5d44 --- /dev/null +++ b/exercises/smelodesousa/F1/1-errors/template.ml @@ -0,0 +1,7 @@ +let quarter_pi = "Replace with your solution" + +let in_order = "Replace with your solution" + +let positive = "Replace with your solution" + +let double_positive = "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-errors/test.ml b/exercises/smelodesousa/F1/1-errors/test.ml new file mode 100644 index 0000000..afdc53c --- /dev/null +++ b/exercises/smelodesousa/F1/1-errors/test.ml @@ -0,0 +1,35 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Correcting question 1" ; + Section ([ Text "quarter_pi: " ; Code "solution" ], + test_variable_against_solution + [%ty: float ] + "quarter_pi") + +let ex2 = + set_progress "Correcting question 2" ; + Section ([ Text "in_order: " ; Code "solution" ], + test_variable_against_solution + [%ty: bool ] + "in_order") + +let ex3 = + set_progress "Correcting question 3" ; + Section ([ Text "positive: " ; Code "solution" ], + test_variable_against_solution + [%ty: bool ] + "positive") + +let ex4 = + set_progress "Correcting question 4" ; + Section ([ Text "double_positive: " ; Code "solution" ], + test_variable_against_solution + [%ty: float ] + "double_positive") + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ ex1; ex2; ex3; ex4 ] diff --git a/exercises/smelodesousa/F1/1-fun-calls/descr.md b/exercises/smelodesousa/F1/1-fun-calls/descr.md new file mode 100644 index 0000000..0c37800 --- /dev/null +++ b/exercises/smelodesousa/F1/1-fun-calls/descr.md @@ -0,0 +1,10 @@ +Let `f` be a function of type `(string * string) -> string`. + +Which of the following expressions is a correct call of `f`? + +A) `f a b c`
+B) `f (a,2)`
+C) `f "hello" "world"`
+D) `f ("hello","world")`

+ +**Note:*** If you believe that the correct option is *`A`* then you should answer as follows: *`let answer = A`. \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-fun-calls/meta.json b/exercises/smelodesousa/F1/1-fun-calls/meta.json new file mode 100644 index 0000000..5278531 --- /dev/null +++ b/exercises/smelodesousa/F1/1-fun-calls/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Function Calls", + "identifier" : "1.12", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F1/1-tuples"] +} diff --git a/exercises/smelodesousa/F1/1-fun-calls/prelude.ml b/exercises/smelodesousa/F1/1-fun-calls/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-fun-calls/prepare.ml b/exercises/smelodesousa/F1/1-fun-calls/prepare.ml new file mode 100644 index 0000000..09f7045 --- /dev/null +++ b/exercises/smelodesousa/F1/1-fun-calls/prepare.ml @@ -0,0 +1,2 @@ +type choice = + | A | B | C | D | To_Answer of string \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-fun-calls/solution.ml b/exercises/smelodesousa/F1/1-fun-calls/solution.ml new file mode 100644 index 0000000..c078158 --- /dev/null +++ b/exercises/smelodesousa/F1/1-fun-calls/solution.ml @@ -0,0 +1 @@ +let answer = D \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-fun-calls/template.ml b/exercises/smelodesousa/F1/1-fun-calls/template.ml new file mode 100644 index 0000000..d129f6e --- /dev/null +++ b/exercises/smelodesousa/F1/1-fun-calls/template.ml @@ -0,0 +1 @@ +let answer = To_Answer "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-fun-calls/test.ml b/exercises/smelodesousa/F1/1-fun-calls/test.ml new file mode 100644 index 0000000..83ed4e0 --- /dev/null +++ b/exercises/smelodesousa/F1/1-fun-calls/test.ml @@ -0,0 +1,12 @@ +open Test_lib +open Report + +let ex1 = + test_variable_against_solution + [%ty: choice ] + "answer" + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + ex1 diff --git a/exercises/smelodesousa/F1/1-future/descr.md b/exercises/smelodesousa/F1/1-future/descr.md new file mode 100644 index 0000000..e3db486 --- /dev/null +++ b/exercises/smelodesousa/F1/1-future/descr.md @@ -0,0 +1,39 @@ + + + + + +Consider the following function `future`: + +```ocaml +let rec future d r = + if d = 0 then r + else + let a = (d lsr 1) in + let b = (string_of_int (d land 1)) in + future a (b ^ r) +``` + + +1. Specify the type of the function `future`. + +2. What value is returned when we run `future 13 ""`? + +3. Assuming that, in the initial call, the `d` parameter is a natural integer and `r` is the empty string `""`, this function calculates: + + A) The function creates a palindrome from the parameter `d`
+ B) The function calculates the number of $1$'s present in the binary representation of `d`
+ C) The function generates the binary representation of `d`
+ D) The function generates a string of $1$'s and $0$'s such that the number of $1$'s is odd if parameter `d` is odd, and the number of $1$'s is even if parameter `d` is even
+ +**Notes:** + - Multiple choice: If you believe that the correct option is `A` then you should answer as follows: `let answer = A`. + - Types: Write the type of the function in the definition of its corresponding type. Example: `type p1 = float -> int`. diff --git a/exercises/smelodesousa/F1/1-future/meta.json b/exercises/smelodesousa/F1/1-future/meta.json new file mode 100644 index 0000000..08d8f70 --- /dev/null +++ b/exercises/smelodesousa/F1/1-future/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Future", + "identifier" : "1.4", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F1/1-mistery"] +} diff --git a/exercises/smelodesousa/F1/1-future/prelude.ml b/exercises/smelodesousa/F1/1-future/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-future/prepare.ml b/exercises/smelodesousa/F1/1-future/prepare.ml new file mode 100644 index 0000000..1468895 --- /dev/null +++ b/exercises/smelodesousa/F1/1-future/prepare.ml @@ -0,0 +1,2 @@ +type choice = + | A | B | C | D | Unanswered of string diff --git a/exercises/smelodesousa/F1/1-future/solution.ml b/exercises/smelodesousa/F1/1-future/solution.ml new file mode 100644 index 0000000..0c6a859 --- /dev/null +++ b/exercises/smelodesousa/F1/1-future/solution.ml @@ -0,0 +1,5 @@ +type p1 = int -> string -> string + +let p2 = "1101" + +let p3 = C \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-future/template.ml b/exercises/smelodesousa/F1/1-future/template.ml new file mode 100644 index 0000000..99e5c5d --- /dev/null +++ b/exercises/smelodesousa/F1/1-future/template.ml @@ -0,0 +1,5 @@ +type p1 = To_Answer_Replace_with_your_solution + +let p2 = failwith "Replace with your solution" + +let p3 = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-future/test.ml b/exercises/smelodesousa/F1/1-future/test.ml new file mode 100644 index 0000000..829bba8 --- /dev/null +++ b/exercises/smelodesousa/F1/1-future/test.ml @@ -0,0 +1,51 @@ +open Test_lib +open Report + +let correct_answer name = + Section ([ Text "Exercise 1: " ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Correct answer" ], Success 5)]) + +let wrong_answer name = + Section ([ Text "Exercise 1: " ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Wrong answer" ], Failure)]) + +let compatible_type ~expected:exp got = + match Introspection.compatible_type exp ("Code." ^ got) with + | Introspection.Absent -> false + | Introspection.Incompatible _ -> false + | Introspection.Present () -> true + +type correct = int -> string -> string +type incorrect1 = float -> string -> string +type incorrect2 = int -> float -> string +type incorrect3 = int -> string -> float + +let ex1 = + let r1 = compatible_type "correct" "p1" in + let r2 = compatible_type "incorrect1" "p1" in + let r3 = compatible_type "incorrect2" "p1" in + let r4 = compatible_type "incorrect3" "p1" in + match r1,r2,r3,r4 with + | true, false, false, false -> correct_answer "p1" + | _ -> wrong_answer "p1" + +let ex2 = + set_progress "Grading exercise 2" ; + Section ([ Text "Exercise 2: " ; Code "solution" ], + test_variable_against_solution + [%ty: string ] + "p2") + +let ex3 = + set_progress "Grading exercise 3" ; + Section ([ Text "Exercise 3: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p3") + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ ex1; ex2; ex3 ] diff --git a/exercises/smelodesousa/F1/1-mistery/descr.md b/exercises/smelodesousa/F1/1-mistery/descr.md new file mode 100644 index 0000000..770556e --- /dev/null +++ b/exercises/smelodesousa/F1/1-mistery/descr.md @@ -0,0 +1,34 @@ + + + + + +Consider the following function `mistery`: + +```ocaml +let rec mistery x y z = + if y <= 0 then z else mistery x ( y - 1) (x * z) +``` + +1. Provide its type. + +2. What is the value returned by `mistery 2 3 1`? + +3. Assuming that the parameters are all natural integers and that the initial value of `z` is $1$, this function determines the values of: + + A) The function: $(x,y) \mapsto y^x$
+ B) The function: $(x,y) \mapsto y^x-1$
+ C) The function: $(x,y) \mapsto x^y$
+ D) The function: $(x,y) \mapsto (x-1)^y$
+ +**Notes:** + - Multiple choice: If you think that the correct option is `A` then you should answer as follows: `let answer = A`. + - Typing: Write the function's type in the corresponding definition. Example: `type p1 = float -> int`. \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-mistery/meta.json b/exercises/smelodesousa/F1/1-mistery/meta.json new file mode 100644 index 0000000..18b151c --- /dev/null +++ b/exercises/smelodesousa/F1/1-mistery/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Mistery", + "identifier" : "1.3", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F1/1-type-error"] +} diff --git a/exercises/smelodesousa/F1/1-mistery/prelude.ml b/exercises/smelodesousa/F1/1-mistery/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-mistery/prepare.ml b/exercises/smelodesousa/F1/1-mistery/prepare.ml new file mode 100644 index 0000000..28c9005 --- /dev/null +++ b/exercises/smelodesousa/F1/1-mistery/prepare.ml @@ -0,0 +1,4 @@ +type choice = + | A | B | C | D | To_Answer of string + +let replace_with_your_solution = max_int \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-mistery/solution.ml b/exercises/smelodesousa/F1/1-mistery/solution.ml new file mode 100644 index 0000000..74e7cea --- /dev/null +++ b/exercises/smelodesousa/F1/1-mistery/solution.ml @@ -0,0 +1,5 @@ +type q1 = int -> int -> int -> int + +let q2 = 8 + +let q3 = C \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-mistery/template.ml b/exercises/smelodesousa/F1/1-mistery/template.ml new file mode 100644 index 0000000..831c116 --- /dev/null +++ b/exercises/smelodesousa/F1/1-mistery/template.ml @@ -0,0 +1,5 @@ +type q1 = To_Answer_Replace_with_your_solution + +let q2 = replace_with_your_solution + +let q3 = To_Answer "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-mistery/test.ml b/exercises/smelodesousa/F1/1-mistery/test.ml new file mode 100644 index 0000000..4a9ab4b --- /dev/null +++ b/exercises/smelodesousa/F1/1-mistery/test.ml @@ -0,0 +1,55 @@ +open Test_lib +open Report + +let correct_answer name = + Section ([ Text "Exercise 1: " ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Correct answer" ], Success 5)]) + +let wrong_answer name = + Section ([ Text "Exercise 1: " ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Wrong answer" ], Failure)]) + +let compatible_type ~expected:exp got = + match Introspection.compatible_type exp ("Code." ^ got) with + | Introspection.Absent -> false + | Introspection.Incompatible _ -> false + | Introspection.Present () -> true + +type correct = int -> int -> int -> int +type incorrect1 = float -> int -> int -> int +type incorrect2 = int -> float -> int -> int +type incorrect3 = int -> int -> float -> int +type incorrect4 = int -> int -> int -> float + + +let ex1 = + let a1 = compatible_type "correct" "q1" in + let a2 = compatible_type "incorrect1" "q1" in + let a3 = compatible_type "incorrect2" "q1" in + let a4 = compatible_type "incorrect3" "q1" in + let a5 = compatible_type "incorrect4" "q1" in + + match a1,a2,a3,a4,a5 with + | true, false, false, false, false -> correct_answer "q1" + | _ -> wrong_answer "q1" + +let ex2 = + set_progress "Correcting question 2" ; + Section ([ Text "Exercise 2: " ; Code "solution" ], + test_variable_against_solution + [%ty: int ] + "q2") + +let ex3 = + set_progress "Correcting question 3" ; + Section ([ Text "Exercise 3: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "q3") + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ ex1; ex2; ex3] diff --git a/exercises/smelodesousa/F1/1-true-false/descr.md b/exercises/smelodesousa/F1/1-true-false/descr.md new file mode 100644 index 0000000..d24d95d --- /dev/null +++ b/exercises/smelodesousa/F1/1-true-false/descr.md @@ -0,0 +1,23 @@ +True or false, the following expressions are well typified. + +1. `let s1 = "a" + "b"` + + A) True
+ B) False

+ +2. `let s2 = "a" ^ "b"` + + A) True
+ B) False

+ +3. `let s3 = 'a' ^ 'b'` + + A) True
+ B) False

+ +4. `let s4 = "a" ^ 'b'` + + A) True
+ B) False

+ +**Note:** If you believe that the correct option is `A` then you should answer as follows: `let answer = A`. \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-true-false/meta.json b/exercises/smelodesousa/F1/1-true-false/meta.json new file mode 100644 index 0000000..ee0616d --- /dev/null +++ b/exercises/smelodesousa/F1/1-true-false/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "True or false?", + "identifier": "1.8", + "authors": [ + ["Rui Barata", "rui.barata@ubi.pt"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F1/1-what-type"] +} diff --git a/exercises/smelodesousa/F1/1-true-false/prelude.ml b/exercises/smelodesousa/F1/1-true-false/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-true-false/prepare.ml b/exercises/smelodesousa/F1/1-true-false/prepare.ml new file mode 100644 index 0000000..227a311 --- /dev/null +++ b/exercises/smelodesousa/F1/1-true-false/prepare.ml @@ -0,0 +1 @@ +type choice = A | B | Unanswered diff --git a/exercises/smelodesousa/F1/1-true-false/solution.ml b/exercises/smelodesousa/F1/1-true-false/solution.ml new file mode 100644 index 0000000..e07e729 --- /dev/null +++ b/exercises/smelodesousa/F1/1-true-false/solution.ml @@ -0,0 +1,4 @@ +let q1 = B +let q2 = A +let q3 = B +let q4 = B \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-true-false/template.ml b/exercises/smelodesousa/F1/1-true-false/template.ml new file mode 100644 index 0000000..d1809ac --- /dev/null +++ b/exercises/smelodesousa/F1/1-true-false/template.ml @@ -0,0 +1,4 @@ +let q1 = Unanswered +let q2 = Unanswered +let q3 = Unanswered +let q4 = Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-true-false/test.ml b/exercises/smelodesousa/F1/1-true-false/test.ml new file mode 100644 index 0000000..ffc9244 --- /dev/null +++ b/exercises/smelodesousa/F1/1-true-false/test.ml @@ -0,0 +1,29 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q1" ) + +let ex2 = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q2" ) + +let ex3 = + set_progress "Grading exercise 3"; + Section + ( [ Text "Exercise 3: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q3" ) + +let ex4 = + set_progress "Grading exercise 4"; + Section + ( [ Text "Exercise 4: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q4" ) + +let () = + set_result @@ ast_sanity_check code_ast @@ fun () -> [ ex1; ex2; ex3; ex4 ] diff --git a/exercises/smelodesousa/F1/1-tuples/descr.md b/exercises/smelodesousa/F1/1-tuples/descr.md new file mode 100644 index 0000000..7839277 --- /dev/null +++ b/exercises/smelodesousa/F1/1-tuples/descr.md @@ -0,0 +1,11 @@ +Consider the following definition: `let n1 = (1,2,3,4,5)` + +How to access exclusively to the fourth element of `n1`? + +A) `n1[3]`
+B) `n1[4]`
+C) `let a,b,c,d,e = n1`
+D) `let a,b,c,_,e = n1`
+E) `let _,_,_,a,_ = n1`
+ +**Note:** If you believe that the correct option is `A` then you should answer as follows: `let answer = A`. \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-tuples/meta.json b/exercises/smelodesousa/F1/1-tuples/meta.json new file mode 100644 index 0000000..a00b4bb --- /dev/null +++ b/exercises/smelodesousa/F1/1-tuples/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Tuples manipulation", + "identifier": "1.11", + "authors": [ + ["Rui Barata", "rui.barata@ubi.pt"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F1/1-type2"] +} diff --git a/exercises/smelodesousa/F1/1-tuples/prelude.ml b/exercises/smelodesousa/F1/1-tuples/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-tuples/prepare.ml b/exercises/smelodesousa/F1/1-tuples/prepare.ml new file mode 100644 index 0000000..5039852 --- /dev/null +++ b/exercises/smelodesousa/F1/1-tuples/prepare.ml @@ -0,0 +1 @@ +type choice = A | B | C | D | E | Unanswered diff --git a/exercises/smelodesousa/F1/1-tuples/solution.ml b/exercises/smelodesousa/F1/1-tuples/solution.ml new file mode 100644 index 0000000..fae41ce --- /dev/null +++ b/exercises/smelodesousa/F1/1-tuples/solution.ml @@ -0,0 +1 @@ +let answer = E \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-tuples/template.ml b/exercises/smelodesousa/F1/1-tuples/template.ml new file mode 100644 index 0000000..201a90f --- /dev/null +++ b/exercises/smelodesousa/F1/1-tuples/template.ml @@ -0,0 +1 @@ +let answer = Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-tuples/test.ml b/exercises/smelodesousa/F1/1-tuples/test.ml new file mode 100644 index 0000000..8e57758 --- /dev/null +++ b/exercises/smelodesousa/F1/1-tuples/test.ml @@ -0,0 +1,8 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise"; + test_variable_against_solution [%ty: choice] "answer" + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> ex1 diff --git a/exercises/smelodesousa/F1/1-type-error/descr.md b/exercises/smelodesousa/F1/1-type-error/descr.md new file mode 100644 index 0000000..9ed2734 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-error/descr.md @@ -0,0 +1,66 @@ +For each of the following items, assign which option is the correct one: + +**Note:** If you believe that the correct option is `A` then you should answer as follows: `let answer = A`. + +1. `let f1 x g z = if x then g x else g z` + + A) bool -> (bool -> 'a) -> bool -> 'a
+ B) bool -> (bool -> 'a) -> bool -> (bool * bool)
+ C) int -> (string -> unit) -> int -> unit
+ D) syntax error
+ E) This expression has type bool but an expression was expected of type unit
+ F) Unbound value g

+ +2. `let f2 f g x = if f x then g x` + + A) ('a -> int) -> ('a -> unit) -> 'a -> int
+ B) ('a -> bool) -> ('a -> unit) -> 'a -> unit
+ C) int -> (int -> bool) -> float -> unit
+ D) This expression has type int but an expression was expected of type unit because it is in the result of a conditional with no else branch
+ E) syntax error
+ F) Unbound value f2

+ +3. `let f3 g x y = if g x then g y` + + A) ('a -> bool) -> 'a -> 'a -> bool
+ B) bool -> int -> int -> bool
+ C) (int -> bool) -> int -> float -> bool
+ D) This expression has type bool but an expression was expected of type unit because it is in the result of a conditional with no else branch
+ E) Stack overflow during evaluation
+ F) Segmentation fault

+ +4. `let rec f4 x y = f4 y ([]::x)` + + A) 'a list list -> 'a list list -> 'b
+ B) 'a array array -> 'a array array -> 'b
+ C) 'a list -> 'a list -> 'b
+ D) Stack overflow during evaluation
+ E) Exception: Invalid_argument
+ F) Exception: Not_Found

+ +5. `let f5 g f x = if x<0 then f (g x) else g (f x)` + + A) ('a -> 'a) -> ('a -> 'a) -> 'a -> 'a
+ B) (float -> int) -> (float -> int) -> float -> int
+ C) (int -> int) -> (int -> int) -> int -> int
+ D) This expression has type bool but an expression was expected of type unit
+ E) This expression has type int but an expression was expected of type unit because it is in the result of a conditional with no else branch
+ F) Unbound value x

+ +6. `let f6 g f x y1 = if x 'a) -> ('a -> 'a) -> 'a -> 'a -> 'a
+ B) (int -> int) -> (int -> int) -> int -> int -> int
+ C) float -> float -> bool -> bool -> float
+ D) This expression has type float but an expression was expected of type int
+ E) It is applied to too many arguments; maybe you forgot a `;`
+ F) Unbound value y

+ +7. `let f7 x y = if x > 0 || y < 0. then x else y` + + A) int -> int -> int
+ B) float -> float -> float
+ C) int -> float -> float
+ D) This expression has type float but an expression was expected of type int
+ E) This expression has type int but an expression was expected of type float
+ F) Stack overflow during evaluation

\ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type-error/meta.json b/exercises/smelodesousa/F1/1-type-error/meta.json new file mode 100644 index 0000000..3c79076 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-error/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Type or error", + "identifier": "1.2", + "authors": [ + ["Rui Barata", "rui.barata@ubi.pt"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F1/1-type"] +} diff --git a/exercises/smelodesousa/F1/1-type-error/prelude.ml b/exercises/smelodesousa/F1/1-type-error/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-type-error/prepare.ml b/exercises/smelodesousa/F1/1-type-error/prepare.ml new file mode 100644 index 0000000..266b5f6 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-error/prepare.ml @@ -0,0 +1 @@ +type choice = A | B | C | D | E | F | Unanswered diff --git a/exercises/smelodesousa/F1/1-type-error/solution.ml b/exercises/smelodesousa/F1/1-type-error/solution.ml new file mode 100644 index 0000000..d8a5c9e --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-error/solution.ml @@ -0,0 +1,7 @@ +let q1 = A +let q2 = B +let q3 = D +let q4 = A +let q5 = C +let q6 = F +let q7 = D \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type-error/template.ml b/exercises/smelodesousa/F1/1-type-error/template.ml new file mode 100644 index 0000000..2992839 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-error/template.ml @@ -0,0 +1,7 @@ +let q1 = Unanswered +let q2 = Unanswered +let q3 = Unanswered +let q4 = Unanswered +let q5 = Unanswered +let q6 = Unanswered +let q7 = Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type-error/test.ml b/exercises/smelodesousa/F1/1-type-error/test.ml new file mode 100644 index 0000000..33c276c --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-error/test.ml @@ -0,0 +1,48 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q1" ) + +let ex2 = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q2" ) + +let ex3 = + set_progress "Grading exercise 3"; + Section + ( [ Text "Exercise 3: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q3" ) + +let ex4 = + set_progress "Grading exercise 4"; + Section + ( [ Text "Exercise 4: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q4" ) + +let ex5 = + set_progress "Grading exercise 5"; + Section + ( [ Text "Exercise 5: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q5" ) + +let ex6 = + set_progress "Grading exercise 6"; + Section + ( [ Text "Exercise 6: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q6" ) + +let ex7 = + set_progress "Grading exercise 7"; + Section + ( [ Text "Exercise 7: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q7" ) + +let () = + set_result @@ ast_sanity_check code_ast + @@ fun () -> [ ex1; ex2; ex3; ex4; ex5; ex6; ex7 ] diff --git a/exercises/smelodesousa/F1/1-type-value/descr.md b/exercises/smelodesousa/F1/1-type-value/descr.md new file mode 100644 index 0000000..44be0ac --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-value/descr.md @@ -0,0 +1,84 @@ +For each question, indicate the type and value or error of the following OCaml expressions: + +**Note:** If you believe that the correct option is `A` then you should answer as follows: `let answer = A`. + +1. `let r = let x = 7 in 6 * x` + + A) int, 42
+ B) int, 6 * x
+ C) int, 7
+ D) syntax error
+ E) Unbound value x
+ F) This expression has type string but an expression was expected of type int

+ +2. `let a = (r - 6) / 6 - 6` + + A) int, 1
+ B) int, 0
+ C) int, -6
+ D) This expression has type float but an expression was expected of type int
+ E) Exception: Division_by_zero.
+ F) Unbound value r

+ +3. `let o = r * r - x * x - 51` + + A) int, -51
+ B) int, 0
+ C) int, r
+ D) Unbound value r
+ E) Unbound value x
+ F) Syntax error: '(' expected

+ +4. `let u = let x = 9 in if (x<9) then 9 / (x-x) else (x+x) / 9` + + A) int, 9
+ B) int, 2
+ C) int, 1
+ D) This expression has type unit but an expression was expected of type int because it is in the result of a conditional with no else branch
+ E) syntax error
+ F) Exception: Division_by_zero.

+ +5. `let x = let a = 10 in if a>7 || b / (a - a) then "hello" else "how are you"` + + A) string, "hello"
+ B) string, "how are youhello"
+ C) string, "how are you"
+ D) Unbound value b
+ E) This expression has type int but an expression was expected of type string
+ F) This expression has type string but an expression was expected of type int

+ +6. `let x = let a = 10 and b = 7 in if a<7 || b / (a - a) > 0 then "hello" else "how are you"` + + A) string, "hello"
+ B) string, "how are you"
+ C) int, 7
+ D) Exception: Division_by_zero.
+ E) This expression has type int but an expression was expected of type bool
+ F) Unbound value b

+ +7. `let x = let a = 10 and b = 7 in if a>7 || b / (a - a) > 0 then "hello" else "how are you"` + + A) string, "hello"
+ B) string, "how are you"
+ C) int, 7
+ D) Exception: Division_by_zero.
+ E) This expression has type int but an expression was expected of type bool
+ F) Unbound value b

+ +8. `let x = let a = 10 in if a < 7 && let b = 1 in b / (a - a) then "hello" else "how are you"` + + A) string, "hello"
+ B) string, "how are you"
+ C) bool, true
+ D) Unbound value b
+ E) This expression has type int but an expression was expected of type bool
+ F) syntax error

+ +9. `let x = let a,b = 10,7 in if a < 7 && a / (b - b) then "hello" else "how are you"` + + A) string, "hello"
+ B) string, "how are you"
+ C) bool, true
+ D) Exception: Division_by_zero.
+ E) syntax error
+ F) This expression has type int but an expression was expected of type bool

diff --git a/exercises/smelodesousa/F1/1-type-value/meta.json b/exercises/smelodesousa/F1/1-type-value/meta.json new file mode 100644 index 0000000..eaef7aa --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-value/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Type and Value", + "identifier" : "1.6", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F1/1-errors"] +} diff --git a/exercises/smelodesousa/F1/1-type-value/prelude.ml b/exercises/smelodesousa/F1/1-type-value/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-type-value/prepare.ml b/exercises/smelodesousa/F1/1-type-value/prepare.ml new file mode 100644 index 0000000..1efd17d --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-value/prepare.ml @@ -0,0 +1,2 @@ +type choice = + | A | B | C | D | E | F | Unanswered of string \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type-value/solution.ml b/exercises/smelodesousa/F1/1-type-value/solution.ml new file mode 100644 index 0000000..b2815d3 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-value/solution.ml @@ -0,0 +1,17 @@ +let p1 = A + +let p2 = F + +let p3 = D + +let p4 = B + +let p5 = D + +let p6 = D + +let p7 = A + +let p8 = E + +let p9 = F \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type-value/template.ml b/exercises/smelodesousa/F1/1-type-value/template.ml new file mode 100644 index 0000000..607a5d8 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-value/template.ml @@ -0,0 +1,17 @@ +let p1 = failwith "Replace with your solution" + +let p2 = failwith "Replace with your solution" + +let p3 = failwith "Replace with your solution" + +let p4 = failwith "Replace with your solution" + +let p5 = failwith "Replace with your solution" + +let p6 = failwith "Replace with your solution" + +let p7 = failwith "Replace with your solution" + +let p8 = failwith "Replace with your solution" + +let p9 = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type-value/test.ml b/exercises/smelodesousa/F1/1-type-value/test.ml new file mode 100644 index 0000000..3976496 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type-value/test.ml @@ -0,0 +1,70 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise 1" ; + Section ([ Text "Exercise 1: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p1") + +let ex2 = + set_progress "Grading exercise 2" ; + Section ([ Text "Exercise 2: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p2") + +let ex3 = + set_progress "Grading exercise 3" ; + Section ([ Text "Exercise 3: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p3") + +let ex4 = + set_progress "Grading exercise 4" ; + Section ([ Text "Exercise 4: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p4") + +let ex5 = + set_progress "Grading exercise 5" ; + Section ([ Text "Exercise 5: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p5") + +let ex6 = + set_progress "Grading exercise 6" ; + Section ([ Text "Exercise 6: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p6") + +let ex7 = + set_progress "Grading exercise 7" ; + Section ([ Text "Exercise 7: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p7") + +let ex8 = + set_progress "Grading exercise 8" ; + Section ([ Text "Exercise 8: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p8") + +let ex9 = + set_progress "Grading exercise 9" ; + Section ([ Text "Exercise 9: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p9") + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ ex1; ex2; ex3; ex4; ex5; ex6; ex7; ex8; ex9] diff --git a/exercises/smelodesousa/F1/1-type/descr.md b/exercises/smelodesousa/F1/1-type/descr.md new file mode 100644 index 0000000..d369a23 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type/descr.md @@ -0,0 +1,12 @@ +What is the type of the following expressions? + +1. `let f x = x * 2` +2. `let f x y = x +. (float y)` +3. `let x = sin 5. in 2.*. x` +4. `let x = 3. in let y = 2 in if x > 1. then 3. ** 9. else cos 1.` +5. `let f x (y,z) = if x then y else not z` +6. `let f x y z = (x <= y, z)` +7. `fun x y z -> (x <= y, if z <= x then x else y)` +8. `let f x y z = (x <= y, if z <= x then x else y)` + +**Note:** Write the functions' type on the corresponding definition. For example: `type q0 = float -> int`. \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type/meta.json b/exercises/smelodesousa/F1/1-type/meta.json new file mode 100644 index 0000000..19e6e6a --- /dev/null +++ b/exercises/smelodesousa/F1/1-type/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Types", + "identifier" : "1.1", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["hferee/1.4_conditionals"] +} diff --git a/exercises/smelodesousa/F1/1-type/prelude.ml b/exercises/smelodesousa/F1/1-type/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-type/prepare.ml b/exercises/smelodesousa/F1/1-type/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-type/solution.ml b/exercises/smelodesousa/F1/1-type/solution.ml new file mode 100644 index 0000000..d986b52 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type/solution.ml @@ -0,0 +1,15 @@ +type ('a, 'b) q1 = int -> int + +type ('a, 'b) q2 = float -> int -> float + +type ('a, 'b) q3 = float + +type ('a, 'b) q4 = float + +type ('a, 'b) q5 = bool -> bool * bool -> bool + +type ('a, 'b) q6 = 'a -> 'a -> 'b -> bool * 'b + +type ('a, 'b) q7 = 'a -> 'a -> 'a -> bool * 'a + +type ('a, 'b) q8 = 'a -> 'a -> 'a -> bool * 'a \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type/template.ml b/exercises/smelodesousa/F1/1-type/template.ml new file mode 100644 index 0000000..831a9b2 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type/template.ml @@ -0,0 +1,15 @@ +type ('a, 'b) q1 = Unanswered + +type ('a, 'b) q2 = Unanswered + +type ('a, 'b) q3 = Unanswered + +type ('a, 'b) q4 = Unanswered + +type ('a, 'b) q5 = Unanswered + +type ('a, 'b) q6 = Unanswered + +type ('a, 'b) q7 = Unanswered + +type ('a, 'b) q8 = Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type/test.ml b/exercises/smelodesousa/F1/1-type/test.ml new file mode 100644 index 0000000..6230c45 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type/test.ml @@ -0,0 +1,144 @@ +open Test_lib +open Report + +let correct_answer id name = + Section + ( [ Text id; Code "solution" ], + [ + Message ([ Text "Grading exercise "; Code name ], Informative); + Message ([ Text "Correct answer" ], Success 5); + ] ) + +let wrong_answer id name = + Section + ( [ Text id; Code "solution" ], + [ + Message ([ Text "Grading exercise "; Code name ], Informative); + Message ([ Text "Wrong answer" ], Failure); + ] ) + +let compatible_type ~expected:exp got = + match Introspection.compatible_type exp ("Code." ^ got) with + | Introspection.Absent -> false + | Introspection.Incompatible _ -> false + | Introspection.Present () -> true + +(* 1 *) +type ('a, 'b) correct_q1 = int -> int +type ('a, 'b) incorrect1_q1 = float -> int +type ('a, 'b) incorrect2_q1 = int -> float + +let ex1 = + let a1 = compatible_type "correct_q1" "q1" in + let a2 = compatible_type "incorrect1_q1" "q1" in + let a3 = compatible_type "incorrect2_q1" "q1" in + + match (a1, a2, a3) with + | true, false, false -> correct_answer "Exercise 1: " "q1" + | _ -> wrong_answer "Exercise 1: " "q1" + +(* 2 *) +type ('a, 'b) correct_q2 = float -> int -> float +type ('a, 'b) incorrect1_q2 = bool -> int -> float +type ('a, 'b) incorrect2_q2 = float -> bool -> float +type ('a, 'b) incorrect3_q2 = float -> int -> bool + +let ex2 = + let a1 = compatible_type "correct_q2" "q2" in + let a2 = compatible_type "incorrect1_q2" "q2" in + let a3 = compatible_type "incorrect2_q2" "q2" in + let a4 = compatible_type "incorrect3_q2" "q2" in + match (a1, a2, a3, a4) with + | true, false, false, false -> correct_answer "Exercise 2: " "q2" + | _ -> wrong_answer "Exercise 2: " "q2" + +(* 3 *) +type ('a, 'b) correct_q3 = float +type ('a, 'b) incorrect_q3 = int + +let ex3 = + let a1 = compatible_type "correct_q3" "q3" in + let a2 = compatible_type "incorrect_q3" "q3" in + match (a1, a2) with + | true, false -> correct_answer "Exercise 3: " "q3" + | _ -> wrong_answer "Exercise 3: " "q3" + +(* 4 *) +type ('a, 'b) correct_q4 = float +type ('a, 'b) incorrect_q4 = int + +let ex4 = + let a1 = compatible_type "correct_q4" "q4" in + let a2 = compatible_type "incorrect_q4" "q4" in + match (a1, a2) with + | true, false -> correct_answer "Exercise 4: " "q4" + | _ -> wrong_answer "Exercise 4: " "q4" + +(* 5 *) +type ('a, 'b) correct_q5 = bool -> bool * bool -> bool +type ('a, 'b) incorrect1_q5 = int -> bool * bool -> bool +type ('a, 'b) incorrect2_q5 = bool -> int * bool -> bool +type ('a, 'b) incorrect3_q5 = bool -> bool * int -> bool +type ('a, 'b) incorrect4_q5 = bool -> bool * bool -> int + +let ex5 = + let a1 = compatible_type "correct_q5" "q5" in + let a2 = compatible_type "incorrect1_q5" "q5" in + let a3 = compatible_type "incorrect2_q5" "q5" in + let a4 = compatible_type "incorrect3_q5" "q5" in + let a5 = compatible_type "incorrect4_q5" "q5" in + match (a1, a2, a3, a4, a5) with + | true, false, false, false, false -> correct_answer "Exercise 5: " "q5" + | _ -> wrong_answer "Exercise 5: " "q5" + +(* 6 *) +type ('a, 'b) correct1_q6 = 'a -> 'a -> 'b -> bool * 'b +type ('a, 'b) correct2_q6 = int -> int -> float -> bool * float +type ('a, 'b) correct3_q6 = float -> float -> int -> bool * int +type ('a, 'b) incorrect_q6 = 'b -> 'b -> 'b -> int * 'b + +let ex6 = + let a1 = compatible_type "correct1_q6" "q6" in + let a2 = compatible_type "correct2_q6" "q6" in + let a3 = compatible_type "correct3_q6" "q6" in + let a4 = compatible_type "incorrect_q6" "q6" in + + match (a1, a2, a3, a4) with + | true, true, true, false -> correct_answer "Exercise 6: " "q6" + | _ -> wrong_answer "Exercise 6: " "q6" + +(* 7 *) +type ('a, 'b) correct1_q7 = 'a -> 'a -> 'a -> bool * 'a +type ('a, 'b) correct2_q7 = int -> int -> int -> bool * int +type ('a, 'b) correct3_q7 = float -> float -> float -> bool * float +type ('a, 'b) incorrect_q7 = 'b -> 'b -> 'b -> int * 'b + +let ex7 = + let a1 = compatible_type "correct1_q7" "q7" in + let a2 = compatible_type "correct2_q7" "q7" in + let a3 = compatible_type "correct3_q7" "q7" in + let a4 = compatible_type "incorrect_q7" "q7" in + + match (a1, a2, a3, a4) with + | true, true, true, false -> correct_answer "Exercise 7: " "q7" + | _ -> wrong_answer "Exercise 7: " "q7" + +(* 8 *) +type ('a, 'b) correct1_q8 = 'a -> 'a -> 'a -> bool * 'a +type ('a, 'b) correct2_q8 = int -> int -> int -> bool * int +type ('a, 'b) correct3_q8 = float -> float -> float -> bool * float +type ('a, 'b) incorrect_q8 = 'b -> 'b -> 'b -> int * 'b + +let ex8 = + let a1 = compatible_type "correct1_q8" "q8" in + let a2 = compatible_type "correct2_q8" "q8" in + let a3 = compatible_type "correct3_q8" "q8" in + let a4 = compatible_type "incorrect_q8" "q8" in + + match (a1, a2, a3, a4) with + | true, true, true, false -> correct_answer "Exercise 8: " "q8" + | _ -> wrong_answer "Exercise 8: " "q8" + +let () = + set_result @@ ast_sanity_check code_ast + @@ fun () -> [ ex1; ex2; ex3; ex4; ex5; ex6; ex7; ex8 ] diff --git a/exercises/smelodesousa/F1/1-type2/descr.md b/exercises/smelodesousa/F1/1-type2/descr.md new file mode 100644 index 0000000..a4a9124 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type2/descr.md @@ -0,0 +1,15 @@ +Consider the following definition: `let a,b = (10, "hello")`. + +1. What is the type and value of `a`? + + A) `int` and `10`
+ B) `string` and `"hello"`
+ C) `Syntax error`

+ +2. What is the type and the value of `b`? + + A) `int` and `10`
+ B) `string` and `"hello"`
+ C) `Syntax error`

+ +**Note:** If you believe the correct option is `A`, then you should answer as follows: `let answer = A`. \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type2/meta.json b/exercises/smelodesousa/F1/1-type2/meta.json new file mode 100644 index 0000000..91686d9 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type2/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Types 2", + "identifier": "1.10", + "authors": [ + ["Rui Barata", "rui.barata@ubi.pt"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F1/1-what-type2"] +} diff --git a/exercises/smelodesousa/F1/1-type2/prelude.ml b/exercises/smelodesousa/F1/1-type2/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-type2/prepare.ml b/exercises/smelodesousa/F1/1-type2/prepare.ml new file mode 100644 index 0000000..ee2ef8f --- /dev/null +++ b/exercises/smelodesousa/F1/1-type2/prepare.ml @@ -0,0 +1,2 @@ +type choice = + | A | B | C | Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type2/solution.ml b/exercises/smelodesousa/F1/1-type2/solution.ml new file mode 100644 index 0000000..fb71321 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type2/solution.ml @@ -0,0 +1,3 @@ +let q1 = A + +let q2 = B \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type2/template.ml b/exercises/smelodesousa/F1/1-type2/template.ml new file mode 100644 index 0000000..a4fe9a7 --- /dev/null +++ b/exercises/smelodesousa/F1/1-type2/template.ml @@ -0,0 +1,3 @@ +let q1 = Unanswered + +let q2 = Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-type2/test.ml b/exercises/smelodesousa/F1/1-type2/test.ml new file mode 100644 index 0000000..3ccab2b --- /dev/null +++ b/exercises/smelodesousa/F1/1-type2/test.ml @@ -0,0 +1,16 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q1" ) + +let ex2 = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q2" ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ ex1; ex2 ] diff --git a/exercises/smelodesousa/F1/1-what-type/descr.md b/exercises/smelodesousa/F1/1-what-type/descr.md new file mode 100644 index 0000000..8e86974 --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type/descr.md @@ -0,0 +1,38 @@ +For each of the following expressions specify which type, from the following choices, is the correct one. + +**Note:** If you believe that the correct option is `A` then you should answer as follows: `let answer = A`. + +1. `let f (x:int) = x + 1` + + A) int -> int
+ B) int -> int -> int
+ C) int -> unit
+ D) unit -> int -> int

+ +2. `let x = 1` + + A) int -> int
+ B) int
+ C) unit -> int
+ D) ERROR

+ +3. `let f x y = x +. y` + + A) int -> int -> int
+ B) unit -> int -> int -> int
+ C) float -> float -> float
+ D) int -> int -> float

+ +4. `let x y = y + 1` + + A) int -> int
+ B) int -> int -> int
+ C) ERROR
+ D) int -> int -> unit

+ +5. `let f1 x y = Printf.printf "%d" x; y + 3` + + A) int -> int -> unit -> int
+ B) int -> int -> int
+ C) unit -> int -> unit -> int
+ D) unit -> int -> int

\ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-what-type/meta.json b/exercises/smelodesousa/F1/1-what-type/meta.json new file mode 100644 index 0000000..77336cd --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "What's the Type?", + "identifier" : "1.7", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F1/1-type-value"] +} diff --git a/exercises/smelodesousa/F1/1-what-type/prelude.ml b/exercises/smelodesousa/F1/1-what-type/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-what-type/prepare.ml b/exercises/smelodesousa/F1/1-what-type/prepare.ml new file mode 100644 index 0000000..3dd5880 --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type/prepare.ml @@ -0,0 +1,2 @@ +type choice = + | A | B | C | D | Unanswered of string \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-what-type/solution.ml b/exercises/smelodesousa/F1/1-what-type/solution.ml new file mode 100644 index 0000000..5479358 --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type/solution.ml @@ -0,0 +1,9 @@ +let p1 = A + +let p2 = B + +let p3 = C + +let p4 = A + +let p5 = B \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-what-type/template.ml b/exercises/smelodesousa/F1/1-what-type/template.ml new file mode 100644 index 0000000..2f39e93 --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type/template.ml @@ -0,0 +1,9 @@ +let p1 = failwith "Replace with your solution" + +let p2 = failwith "Replace with your solution" + +let p3 = failwith "Replace with your solution" + +let p4 = failwith "Replace with your solution" + +let p5 = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-what-type/test.ml b/exercises/smelodesousa/F1/1-what-type/test.ml new file mode 100644 index 0000000..3ca6b2d --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type/test.ml @@ -0,0 +1,42 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise 1" ; + Section ([ Text "Exercise 1: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p1") + +let ex2 = +set_progress "Grading exercise 2" ; +Section ([ Text "Exercise 2: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p2") + +let ex3 = +set_progress "Grading exercise 3" ; +Section ([ Text "Exercise 3: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p3") + +let ex4 = +set_progress "Grading exercise 4" ; +Section ([ Text "Exercise 4: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p4") + +let ex5 = +set_progress "Grading exercise 5" ; +Section ([ Text "Exercise 5: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p5") + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ ex1; ex2; ex3; ex4; ex5 ] diff --git a/exercises/smelodesousa/F1/1-what-type2/descr.md b/exercises/smelodesousa/F1/1-what-type2/descr.md new file mode 100644 index 0000000..798e7c4 --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type2/descr.md @@ -0,0 +1,32 @@ +For each of the following items, assign which option is the correct one: + +**Note:** If you believe the correct option is `A`, then you should answer as follows: `let answer = A`. + +1. `let f x = if x = 2 then Printf.printf "x = 2" else Printf.printf "x <> 2"` + + A) `int -> int -> unit`
+ B) `int -> int -> unit -> unit`
+ C) `int -> unit`
+ D) `unit`
+ E) `ERROR`

+ +2. `let f x y = if x = 2. then x else y+3` + + A) `int -> int -> int`
+ B) `float -> int -> float -> int`
+ C) `float -> int -> unit`
+ D) `ERROR`

+ +3. `let f x = let y = x +. 4. /. 10. in if y > 3.1 then y else 3.` + + A) `int -> int`
+ B) `float -> unit -> float`
+ C) `float -> float`
+ D) `ERROR`

+ +4. `let f = (if 2 > 3 then true else true) || false` + + A) `bool -> bool`
+ B) `bool`
+ C) `int -> int -> bool`
+ D) `ERROR`

\ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-what-type2/meta.json b/exercises/smelodesousa/F1/1-what-type2/meta.json new file mode 100644 index 0000000..e49b401 --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type2/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "What is the type? 2", + "identifier" : "1.9", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F1/1-true-false"] +} diff --git a/exercises/smelodesousa/F1/1-what-type2/prelude.ml b/exercises/smelodesousa/F1/1-what-type2/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F1/1-what-type2/prepare.ml b/exercises/smelodesousa/F1/1-what-type2/prepare.ml new file mode 100644 index 0000000..8b724b2 --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type2/prepare.ml @@ -0,0 +1,2 @@ +type choice = + | A | B | C | D | E | Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-what-type2/solution.ml b/exercises/smelodesousa/F1/1-what-type2/solution.ml new file mode 100644 index 0000000..0d76a4b --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type2/solution.ml @@ -0,0 +1,7 @@ +let q1 = C + +let q2 = D + +let q3 = C + +let q4 = B \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-what-type2/template.ml b/exercises/smelodesousa/F1/1-what-type2/template.ml new file mode 100644 index 0000000..9f34260 --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type2/template.ml @@ -0,0 +1,7 @@ +let q1 = Unanswered + +let q2 = Unanswered + +let q3 = Unanswered + +let q4 = Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F1/1-what-type2/test.ml b/exercises/smelodesousa/F1/1-what-type2/test.ml new file mode 100644 index 0000000..ffc9244 --- /dev/null +++ b/exercises/smelodesousa/F1/1-what-type2/test.ml @@ -0,0 +1,29 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q1" ) + +let ex2 = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q2" ) + +let ex3 = + set_progress "Grading exercise 3"; + Section + ( [ Text "Exercise 3: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q3" ) + +let ex4 = + set_progress "Grading exercise 4"; + Section + ( [ Text "Exercise 4: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q4" ) + +let () = + set_result @@ ast_sanity_check code_ast @@ fun () -> [ ex1; ex2; ex3; ex4 ] diff --git a/exercises/smelodesousa/F3/3-a-historic-algorithm/descr.md b/exercises/smelodesousa/F3/3-a-historic-algorithm/descr.md new file mode 100644 index 0000000..64553c6 --- /dev/null +++ b/exercises/smelodesousa/F3/3-a-historic-algorithm/descr.md @@ -0,0 +1,27 @@ + + + + + + +# Introduction + +In this exercise, we will implement a historic algorithm formulated by Euclid himself in 300 BC. +In particular, we will work with the recursive version: + +
+$gcd(a, b) ={(a,if\ b=0), (gcd(b,mod(a,b)), otherwise\):}$ +
+ + +# Objectives + +Define a function `euclid : int -> int -> int` that, given two non-negative integers, determines their greatest common divisor based on the algorithm above. Thus, `euclid 36 45 = 9`. In case of an invalid argument, the exception `Invalid_argument "euclid"` is thrown. diff --git a/exercises/smelodesousa/F3/3-a-historic-algorithm/meta.json b/exercises/smelodesousa/F3/3-a-historic-algorithm/meta.json new file mode 100644 index 0000000..a88a287 --- /dev/null +++ b/exercises/smelodesousa/F3/3-a-historic-algorithm/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "A historic algorithm", + "identifier": "3.3", + "authors": [ + ["Dário Santos", "dariovfsantos@gmail.com"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F3/3-manhattan-distance"] +} diff --git a/exercises/smelodesousa/F3/3-a-historic-algorithm/prelude.ml b/exercises/smelodesousa/F3/3-a-historic-algorithm/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-a-historic-algorithm/prepare.ml b/exercises/smelodesousa/F3/3-a-historic-algorithm/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-a-historic-algorithm/solution.ml b/exercises/smelodesousa/F3/3-a-historic-algorithm/solution.ml new file mode 100644 index 0000000..33f88da --- /dev/null +++ b/exercises/smelodesousa/F3/3-a-historic-algorithm/solution.ml @@ -0,0 +1,12 @@ +let rec euclid a b = + match (a, b) with + | invalid_a, invalid_b when a < 0 || b < 0 -> + raise (Invalid_argument "euclid") + | _, 0 -> a + | _ -> euclid b (a mod b) + +(* Original version: + let rec euclid a b = + if a < 0 || b < 0 then raise (Invalid_argument "euclid"); + if b = 0 then a + else euclid b (a mod b) *) diff --git a/exercises/smelodesousa/F3/3-a-historic-algorithm/template.ml b/exercises/smelodesousa/F3/3-a-historic-algorithm/template.ml new file mode 100644 index 0000000..230d67f --- /dev/null +++ b/exercises/smelodesousa/F3/3-a-historic-algorithm/template.ml @@ -0,0 +1 @@ +let rec euclid a b = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-a-historic-algorithm/test.ml b/exercises/smelodesousa/F3/3-a-historic-algorithm/test.ml new file mode 100644 index 0000000..d91020c --- /dev/null +++ b/exercises/smelodesousa/F3/3-a-historic-algorithm/test.ml @@ -0,0 +1,69 @@ +(* + Euclid function: a, b contained in [0, +infinity[ + + Test Cases: + + + | Test Case | a | b | + | :-------: | :------: | :-------: | + | T1 | 0 | 0 | + | T2 | 0 | non-zero | + | T3 | non-zero | 0 | + | T4 | positive | positive | + | T5 | positive | negative | + | T6 | negative | positive | + | T7 | negative | negative | + + | Test Case | a | b | + | :-------: | :---: | :---: | + | T1 | 0 | 0 | + | T2 | 0 | -1 | + | T3 | -1 | 0 | + | T4 | 1 | 1 | + | T5 | 1 | -1 | + | T6 | -1 | 1 | + | T7 | -1 | -1 | +*) + +open Test_lib +open Report + +let test_with_solution = + Section + ( [ Text "Tests" ], + test_function_2_against_solution [%ty: int -> int -> int] "euclid" + ~sampler:(fun () -> (Random.int 999999, Random.int 999999)) + ~gen:13 + [ (0, 0) ] ) + +let test_with_solution_both_negative = + Section + ( [ Text "Tests" ], + test_function_2_against_solution [%ty: int -> int -> int] "euclid" + ~sampler:(fun () -> + (Random.int 999999 - 999999, Random.int 999999 - 999999)) + ~gen:2 [] ) + +let test_with_solution_a_negative = + Section + ( [ Text "Tests" ], + test_function_2_against_solution [%ty: int -> int -> int] "euclid" + ~sampler:(fun () -> (Random.int 999999 - 999999, Random.int 999999)) + ~gen:2 [] ) + +let test_with_solution_b_negative = + Section + ( [ Text "Tests" ], + test_function_2_against_solution [%ty: int -> int -> int] "euclid" + ~sampler:(fun () -> (Random.int 999999, Random.int 999999 - 999999)) + ~gen:2 [] ) + +let () = + set_result @@ ast_sanity_check code_ast + @@ fun () -> + [ + test_with_solution; + test_with_solution_both_negative; + test_with_solution_a_negative; + test_with_solution_b_negative; + ] diff --git a/exercises/smelodesousa/F3/3-catalan/descr.md b/exercises/smelodesousa/F3/3-catalan/descr.md new file mode 100644 index 0000000..e013866 --- /dev/null +++ b/exercises/smelodesousa/F3/3-catalan/descr.md @@ -0,0 +1,34 @@ + + + + + + +# Introduction + +The famous integer sequence known as the *_Catalan Number_* (from the Belgian mathematician Eugène Charles Catalan, 1814–1894) is defined by: + +
$ +Catalan(n) = { +(1, if n=0 vv n=1), +(\sum_{(p,q)\ such\ t h at\ p+q=n-1} Catalan(p)*Catalan(q), if n>1 +):} +$
+ +This sequence is seen in countless combinatorial problems. + +# Objective + +Write a function `catalan : int -> int` that takes in a natural integer $n$ and recursively calculates the value of $Catalan(n)$. + +In the case of an invalid argument, the `Invalid_argument "catalan"` exception is thrown. + +As an example, `catalan 6 = 132`. \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-catalan/meta.json b/exercises/smelodesousa/F3/3-catalan/meta.json new file mode 100644 index 0000000..36e4d72 --- /dev/null +++ b/exercises/smelodesousa/F3/3-catalan/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Catalan: a language, a desert and some numbers", + "identifier" : "3.13", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Melo de Sousa",""], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F3/3-fast-exponentiation"] +} diff --git a/exercises/smelodesousa/F3/3-catalan/prelude.ml b/exercises/smelodesousa/F3/3-catalan/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-catalan/prepare.ml b/exercises/smelodesousa/F3/3-catalan/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-catalan/solution.ml b/exercises/smelodesousa/F3/3-catalan/solution.ml new file mode 100644 index 0000000..1d6e6ff --- /dev/null +++ b/exercises/smelodesousa/F3/3-catalan/solution.ml @@ -0,0 +1,4 @@ +let rec catalan = function + | 0 | 1 -> 1 + | n when n > 1 -> catalan (n - 1) * 2 * (2 * n - 1) / (n + 1) + | _ -> invalid_arg "catalan" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-catalan/template.ml b/exercises/smelodesousa/F3/3-catalan/template.ml new file mode 100644 index 0000000..098e733 --- /dev/null +++ b/exercises/smelodesousa/F3/3-catalan/template.ml @@ -0,0 +1,2 @@ +let rec catalan n = + failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-catalan/test.ml b/exercises/smelodesousa/F3/3-catalan/test.ml new file mode 100644 index 0000000..605a642 --- /dev/null +++ b/exercises/smelodesousa/F3/3-catalan/test.ml @@ -0,0 +1,32 @@ +open Test_lib +open Report + +let check_recursion name cb = + let module Error = struct exception RecursionCall end in + + find_binding code_ast name @@ fun expr -> + let contains_recursion_call = Parsetree.(function + | {pexp_desc = Pexp_apply ({pexp_desc = Pexp_ident {txt = id}}, _)} -> + if (Longident.last id) = name then raise Error.RecursionCall else [] + | _ -> []) in + try + ast_check_expr ~on_expression:contains_recursion_call expr; + [Message ([Text "The function"; Code name; Text "does not contain a recursive call"], Failure)] + with Error.RecursionCall -> cb () + +let int_sampler () = + let () = Random.self_init () in + ((Random.int 31) - 15) + +let catalanS () = + check_recursion "catalan" @@ fun () -> test_function_1_against_solution + [%ty: int -> int ] + "catalan" + ~sampler: int_sampler + ~gen: 9 + [ -100; 0 ] + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + catalanS () \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-collatz-hailstones/descr.md b/exercises/smelodesousa/F3/3-collatz-hailstones/descr.md new file mode 100644 index 0000000..126bd51 --- /dev/null +++ b/exercises/smelodesousa/F3/3-collatz-hailstones/descr.md @@ -0,0 +1,37 @@ + + + + + +# Introduction + +The Collatz conjecture (or Hailstones, or Syracuse) was thought of by the german mathematician Lothar Collatz as a challenge for the scientific community during an event at the University of Syracuse in 1928. The conjecture defines a number sequence (also referred to as trajectory or flight) that, starting on a natural integer, obeys the following principles: + +- if *n* is even, then the successor of *n* in the sequence is *n* divided by 2 +- if *n* is odd, then the successor of *n* in the sequence is multiplied by 3, plus 1. +- if the sequence reaches the number 1, then we stop. + +To this day, nobody has found an initial value *n* such that the trajectory doesn't finish on the number $1$! (without the stopping condition, we would get an infinite loop starting on 1) + +Examples: + +
+$6 \to 3 \to 10 \to 5 \to 16 \to 8 \to 4 \to 2 \to 1$ +
+
+$17 \to 52 \to 26 \to 13 \to 40 \to 20 \to 10 \to 5 \to 16 \to \cdots \to 1$ +
+ +# Objective + +Your challenge is to write a recursive function `collatz : int -> int list` in OCaml that, given a parameter *n*, returns the sequence of integers corresponding to the trajectory calculated from the value *n*. Obviously, this sequence stops when it reaches the value 1. For example: + +`collatz 6 = [6; 3; 10; 5; 16; 8; 4; 2; 1]` \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-collatz-hailstones/meta.json b/exercises/smelodesousa/F3/3-collatz-hailstones/meta.json new file mode 100644 index 0000000..17858d2 --- /dev/null +++ b/exercises/smelodesousa/F3/3-collatz-hailstones/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "The Collatz conjecture, or Hailstones, or Syracuse and other cool names...", + "identifier" : "3.17", + "authors" : [["Leonardo Santos", "leomendesantos@gmail.com"], ["Dário Santos","dariovfsantos@gmail.com"], ["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F3/3-triangles"] +} diff --git a/exercises/smelodesousa/F3/3-collatz-hailstones/prelude.ml b/exercises/smelodesousa/F3/3-collatz-hailstones/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-collatz-hailstones/prepare.ml b/exercises/smelodesousa/F3/3-collatz-hailstones/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-collatz-hailstones/solution.ml b/exercises/smelodesousa/F3/3-collatz-hailstones/solution.ml new file mode 100644 index 0000000..d31d226 --- /dev/null +++ b/exercises/smelodesousa/F3/3-collatz-hailstones/solution.ml @@ -0,0 +1,4 @@ +let rec collatz = function + | 1 -> [1] + | n when n mod 2 = 0 -> n :: (collatz (n / 2)) + | n -> n :: (collatz ((3 * n) + 1)) \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-collatz-hailstones/template.ml b/exercises/smelodesousa/F3/3-collatz-hailstones/template.ml new file mode 100644 index 0000000..7acb9bc --- /dev/null +++ b/exercises/smelodesousa/F3/3-collatz-hailstones/template.ml @@ -0,0 +1 @@ +let rec collatz n = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-collatz-hailstones/test.ml b/exercises/smelodesousa/F3/3-collatz-hailstones/test.ml new file mode 100644 index 0000000..c6b8fdd --- /dev/null +++ b/exercises/smelodesousa/F3/3-collatz-hailstones/test.ml @@ -0,0 +1,38 @@ +open Test_lib +open Report +open Random + +(* + check_recursion name cb + + val name: string + + Checks if function name is recursive. Check_recursion checks + if there's a function call to name inside the function name. +*) +let check_recursion name cb = + let module Error = struct exception RecursionCall end in + + find_binding code_ast name @@ fun expr -> + let contains_recursion_call = Parsetree.(function + | {pexp_desc = Pexp_apply ({pexp_desc = Pexp_ident {txt = id}}, _)} -> + if (Longident.last id) = name then raise Error.RecursionCall else [] + | _ -> []) in + try + ast_check_expr ~on_expression:contains_recursion_call expr; + [Message ([Text "The function"; Code name; Text "does not contain a recursive call"], Failure)] + with Error.RecursionCall -> cb () + +let test_collatz = Section( + [Text "Testing"; Code "collatz"], + check_recursion "collatz" @@ fun () -> test_function_1_against_solution + [%ty: int -> int list] + "collatz" + ~sampler: (fun () -> let () = Random.self_init () in Random.int(30)+1) + ~gen: 10 + []) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [test_collatz] \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-dichotomy/descr.md b/exercises/smelodesousa/F3/3-dichotomy/descr.md new file mode 100644 index 0000000..55d35a1 --- /dev/null +++ b/exercises/smelodesousa/F3/3-dichotomy/descr.md @@ -0,0 +1,47 @@ + + + + + +# Introduction + +Let `v` be an *ascending-ordered* vector of integers of size `n`, and `x` an integer. + +We will define an *efficient* search function that takes advantage of vector sorting. + +Consider two indices `i`, `j` of vector `v` such that $i\leq j$. We will look for x in the vector `v` between the indices i and j, (notation $v[i \ldots j]$) + +- If $i=j$ then $x\in v[i\ldots j]$ if and only if $x = v[i]$. +Otherwise: + +- If $v[i] > x$ then we know that $x$ is not in the $[i,j]$ segment of the vector $v$. In the best case, it is somewhere in the $[0,i[$ segment of the $v$ vector. + +- If $x < v[j]$ then we know that $x$ is not in the $[i,j]$ segment of the vector $v$. In the best case, it is somewhere in the $]j,n[$ segment of the $v$ vector. + +- If $v[i] \leq x \leq v[j]$ then we know that $x$ is possibly in the segment $[i,j]$ of the vector $v$. What we know for sure is that x is neither in $v[0 \ldots i-1]$ nor $v[j \ldots n]$. Consequently, the search must be focused on the segment $[i,j]$. + + We have $i v[m]$, then the segment $v[i\ldots m]$ of the vector $v$ does not contain x. Possibly it will be in $v[m+1\ldots j]$. + - If $x < v[m]$, then the segment $v[m \ldots j]$ of the vector $v$ does not contain x. Possibly it will be in $v[i\ldots m-1]$. + + This method is called *binary search* or *dichotomous search*. + +# Objectives + +1. Define a recursive binary search function `binsearch_aux : 'a -> 'a array -> int -> int -> int` . `binsearch_aux x v low high` search for the value `x` in the ordered vector `v` between indexes `low` and `high`. This function returns the index where the `x` value lies in `v` (in the `low..high` range), or else the exception `Not_found` is thrown in any other situation. + + For instance, `binsearch_aux 12 [|1;2;5;7;12;16;23;33;78|] 2 6` returns `4`. + +2. Define a `binsearch function: 'a -> 'a array -> int` that searches for the value `x` in the entire sorted array `v`. This function returns the index where the value `x` is in `v` or the exception `Not_found` is thrown. It is recommended to use the function from the previous exercise. + + Note that this search divides the search space by two each time the search is refined. This feature greatly improves response times. The worst case is when the element to be searched is not present in the vector. Nevertheless, the number of comparisons performed by the algorithm never exceeds the order of $log_2(n)$ where $n$ is the size of the vector. \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-dichotomy/meta.json b/exercises/smelodesousa/F3/3-dichotomy/meta.json new file mode 100644 index 0000000..ebc3837 --- /dev/null +++ b/exercises/smelodesousa/F3/3-dichotomy/meta.json @@ -0,0 +1,13 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Dichotomy", + "identifier": "3.14", + "authors": [ + ["Dário Santos", "dariovfsantos@gmail.com"], + ["Gonçalo Domingos", "goncalogdomingos@gmail.com"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F3/3-how-many-mountains"] +} diff --git a/exercises/smelodesousa/F3/3-dichotomy/prelude.ml b/exercises/smelodesousa/F3/3-dichotomy/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-dichotomy/prepare.ml b/exercises/smelodesousa/F3/3-dichotomy/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-dichotomy/solution.ml b/exercises/smelodesousa/F3/3-dichotomy/solution.ml new file mode 100644 index 0000000..6cb513c --- /dev/null +++ b/exercises/smelodesousa/F3/3-dichotomy/solution.ml @@ -0,0 +1,8 @@ +(* 1 *) +let rec binsearch_aux (x : int) v low high = + if low > high then raise Not_found + else if v.(low) = x then low + else binsearch_aux x v (low + 1) high + +(* 2 *) +let binsearch x v = binsearch_aux x v 0 (Array.length v - 1) diff --git a/exercises/smelodesousa/F3/3-dichotomy/template.ml b/exercises/smelodesousa/F3/3-dichotomy/template.ml new file mode 100644 index 0000000..ebbea8d --- /dev/null +++ b/exercises/smelodesousa/F3/3-dichotomy/template.ml @@ -0,0 +1,2 @@ +let rec binsearch_aux x v low high = failwith "Unanswered" +let binsearch x v = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-dichotomy/test.ml b/exercises/smelodesousa/F3/3-dichotomy/test.ml new file mode 100644 index 0000000..29b5c88 --- /dev/null +++ b/exercises/smelodesousa/F3/3-dichotomy/test.ml @@ -0,0 +1,85 @@ +open Test_lib +open Report +open List +open Random +open Solution + +(* + check_recursion name cb + + val name: string + + Checks if function name is recursive. Check_recursion checks + if there's a function call to name inside the function name. +*) +let check_recursion name cb = + let module Error = struct + exception RecursionCall + end in + find_binding code_ast name @@ fun expr -> + let contains_recursion_call = + Parsetree.( + function + | { pexp_desc = Pexp_apply ({ pexp_desc = Pexp_ident { txt = id } }, _) } + -> + if Longident.last id = name then raise Error.RecursionCall else [] + | _ -> []) + in + try + ast_check_expr ~on_expression:contains_recursion_call expr; + [ + Message + ( [ + Text "The function"; + Code name; + Text "does not contain a recursive call"; + ], + Failure ); + ] + with Error.RecursionCall -> cb () + +let vector_gen () = + let () = Random.self_init () in + let length = Random.int 3 + 14 in + let v = Array.make length 0 in + for i = 0 to length - 1 do + v.(i) <- Random.int 4 + ((2 * i * i) + i) + done; + v + +let get_value () = + let () = Random.self_init () in + let vec = vector_gen () in + if Random.int 10 >= 3 then + ( vec.(6 + Random.int (Array.length vec - 8)), + vec, + 6, + Array.length vec - 1 - 2 ) + else (900, vec, 0, Array.length vec) + +let get_value2 () = + let () = Random.self_init () in + let vec = vector_gen () in + if Random.int 10 >= 3 then (vec.(Random.int (Array.length vec - 1)), vec) + else (900, vec) + +let test_1 = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "binsearch_aux" ], + check_recursion "binsearch_aux" @@ fun () -> + test_function_4_against_solution + [%ty: int -> int array -> int -> int -> int] "binsearch_aux" + ~sampler:get_value ~gen:10 + [ (12, [| 1; 2; 5; 7; 12; 16; 23; 33; 78 |], 2, 6); (1, [| 1 |], 1, 1) ] + ) + +let testT = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "binsearch" ], + test_function_2_against_solution [%ty: int -> int array -> int] + "binsearch" ~sampler:get_value2 ~gen:10 + [ (12, [| 1; 2; 5; 7; 12; 16; 23; 33; 78 |]); (1, [| 1 |]) ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ test_1; testT ] diff --git a/exercises/smelodesousa/F3/3-digits/descr.md b/exercises/smelodesousa/F3/3-digits/descr.md new file mode 100644 index 0000000..7531e04 --- /dev/null +++ b/exercises/smelodesousa/F3/3-digits/descr.md @@ -0,0 +1,16 @@ + + + + + +Implement a function `digits : int -> int -> int*int*int` which receives an integer $n$ and a digit $i$, and returns the number of times the digit $i$ appears in $n$, the sum of the digits of $n$ and finally the number of digits of $n$. + +For example, `digits 1073741823 3` would return the tuple `(2,36,10)`. diff --git a/exercises/smelodesousa/F3/3-digits/meta.json b/exercises/smelodesousa/F3/3-digits/meta.json new file mode 100644 index 0000000..ee4f050 --- /dev/null +++ b/exercises/smelodesousa/F3/3-digits/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Digits", + "identifier" : "3.6", + "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F3/3-pi"] +} diff --git a/exercises/smelodesousa/F3/3-digits/prelude.ml b/exercises/smelodesousa/F3/3-digits/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-digits/prepare.ml b/exercises/smelodesousa/F3/3-digits/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-digits/solution.ml b/exercises/smelodesousa/F3/3-digits/solution.ml new file mode 100644 index 0000000..9b2e118 --- /dev/null +++ b/exercises/smelodesousa/F3/3-digits/solution.ml @@ -0,0 +1,10 @@ +let (+) (x1,y1,z1) (x2,y2,z2) = (x1 + x2, y1 + y2, z1 + z2) + +let is_equal n i = if (n mod 10) = i then 1 else 0 + +let rec digits n i = + let rec digits_aux n i = + if n = 0 then (0,0,0) + else ((is_equal n i), n mod 10, 1) + (digits_aux (n / 10) i) + in + if (is_equal n i) = 1 && (n = 0) then (1,0,1) else digits_aux n i diff --git a/exercises/smelodesousa/F3/3-digits/template.ml b/exercises/smelodesousa/F3/3-digits/template.ml new file mode 100644 index 0000000..fb2a7a8 --- /dev/null +++ b/exercises/smelodesousa/F3/3-digits/template.ml @@ -0,0 +1 @@ +let rec digits n i = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-digits/test.ml b/exercises/smelodesousa/F3/3-digits/test.ml new file mode 100644 index 0000000..d3e7717 --- /dev/null +++ b/exercises/smelodesousa/F3/3-digits/test.ml @@ -0,0 +1,18 @@ +open Test_lib +open Report + + +let test_with_solution = + Section([Text "Tests"], + test_function_2_against_solution + [%ty: int -> int -> int*int*int] + "digits" + ~sampler: (fun () -> (Random.int 999999999), (Random.int 10)) + ~gen:19 + [(0, 0)] + ) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [test_with_solution] \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-dragon-greatness/descr.md b/exercises/smelodesousa/F3/3-dragon-greatness/descr.md new file mode 100644 index 0000000..73e60e1 --- /dev/null +++ b/exercises/smelodesousa/F3/3-dragon-greatness/descr.md @@ -0,0 +1,65 @@ + + + + + +# Introduction + +Imagine the following situation. + +There is a paper ribbon that you will want to fold in half n times. + +The initial configuration is (unfolded paper, profile view): + +![](https://i.imgur.com/FrKFeHH.png) + + +If you fold it once and unfold it and let the angle make 90º, you get the following figure in profile: + +![](https://i.imgur.com/FdNQ01N.png) + +If you fold it twice and unfold it and let the angles obtained make 90º, you get the following figure in profile: + +![](https://i.imgur.com/SKXenGJ.png) + +If you fold it three times and unfold it and let the angles obtained make 90º, you get the following figure in profile: + +![](https://i.imgur.com/ekQh8LV.png) + + +Very quickly, the paper folding exercise becomes tedious but visually curious (taken from (Wikipedia)): + + +![](https://i.imgur.com/4RtPGWP.gif) + + +The fractal obtained is called *dragon curve*. + +The following image (also taken from Wikipedia) shows how to get a configuration from the previous one. + +![](https://i.imgur.com/EY1Z8LP.png) + +Let's encode these pictures with binary words. The principle is: "an angle to the left is 1" and "an angle to the right is 0". Thus: + +- The tape with *zero* folds is represented by the empty word, $\epsilon$; +- The tape with *one* fold in the middle is encoded by the word $0$; +- The tape with *two* folds in the middle is encoded by the word $001$; +- The tape with *three* folds in the middle is encoded by the word $0010011$. + +# Objectives + +In this exercise, we are interested in answering two questions: what is the word obtained after n folds? what is the $m$-th letter of the word? To answer this, let's program. + +1. Define a function `dragon_size: int -> bool list` which returns the size of the word after $n$ (given in parameter, a positive integer, possibly null) folds in the middle. For example, `dragon_size 4 = 15`. In case of an invalid argument, the exception `Invalid_argument "dragon_size"` is thrown. + +2. Define a function `dragon: int -> bool list` which returns the list of booleans that form the dragon curve word for n (in parameter) folds. For example, `dragon 3 = [false; false; true; false; false; true; true]`. In case of an invalid argument, the exception `Invalid_argument "dragon"` is thrown. + +3. Define a function `dragon_bit : int -> bool` which for a nonzero positive integer $n$ (in parameter) returns the $n$-th bit of the dragon sequence. For example, `dragon_bit 11 = true`. In case of an invalid argument, the exception `Invalid_argument "dragon_bit"` is thrown. \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-dragon-greatness/meta.json b/exercises/smelodesousa/F3/3-dragon-greatness/meta.json new file mode 100644 index 0000000..cde3f80 --- /dev/null +++ b/exercises/smelodesousa/F3/3-dragon-greatness/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 3, + "title": "The dragon's greatness", + "identifier": "3.21", + "authors": [ + ["Dário Santos", "dariovfsantos@gmail.com"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["hferee/10_assoc"] +} diff --git a/exercises/smelodesousa/F3/3-dragon-greatness/prelude.ml b/exercises/smelodesousa/F3/3-dragon-greatness/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-dragon-greatness/prepare.ml b/exercises/smelodesousa/F3/3-dragon-greatness/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-dragon-greatness/solution.ml b/exercises/smelodesousa/F3/3-dragon-greatness/solution.ml new file mode 100644 index 0000000..1ae784d --- /dev/null +++ b/exercises/smelodesousa/F3/3-dragon-greatness/solution.ml @@ -0,0 +1,106 @@ +(* Solution for 1 + let dragon_size n = + if n >= 0 then (1 lsl n) - 1 + else raise (Invalid_argument "dragon_size") + + (* Solution for 2 *) + let append l1 l2 = + let rec aux acc = function + | [], [] -> List.rev acc + | [], h :: t -> aux (h :: acc) ([], t) + | h :: t, l -> aux (h :: acc) (t, l) + in + aux [] (l1, l2) + + let rec dragon_aux n t = + if n <= 1 then t + else + let tnew = append t [false] in + let t = List.fold_left(fun acc e -> (not e)::acc) [] t in + dragon_aux (n-1) (append tnew t) + + let dragon = function + | 0 -> [] + | n when n > 0 -> dragon_aux n [false] + | _ -> raise (Invalid_argument "dragon") + + (* Solution for 3 *) + let closest_pow2 v = + let v = v - 1 in + let v = v lor (v lsr 1) in + let v = v lor (v lsr 2) in + let v = v lor (v lsr 4) in + let v = v lor (v lsr 8) in + let v = v lor (v lsr 16) in + v + 1 + + let dragon_bit = function + | n when n > 0 -> + let v = closest_pow2 n in + List.nth (dragon v) (n-1) + | _ -> raise (Invalid_argument "dragon_bit") +*) + +(* Another possible solution *) +(* 1 *) +let dragon_size n = + if n < 0 then raise (Invalid_argument "dragon_size") else (1 lsl n) - 1 + +(* 2 *) +let rec remove_middle l = + if List.length l = 1 then [] + else + let middle = List.length l / 2 in + let rec loop acc i = function + | [] -> List.rev acc + | h :: t -> + if i = middle then loop acc (i + 1) t else loop (h :: acc) (i + 1) t + in + loop [] 0 l + +let append l1 l2 = + let rec loop acc l1 l2 = + match (l1, l2) with + | [], [] -> List.rev acc + | [], h :: t -> loop (h :: acc) [] t + | h :: t, l -> loop (h :: acc) t l + in + loop [] l1 l2 + +let add_false l = append l [ false ] +let add_true l = append l [ true ] + +let list_copy_index l index_beg index_end = + let rec loop acc i = function + | [] -> List.rev acc + | h :: t -> + if i >= index_beg && i <= index_end then loop (h :: acc) (i + 1) t + else loop acc (i + 1) t + in + loop [] 0 l + +let dragon n = + if n < 0 then raise (Invalid_argument "dragon") + else if n = 0 then [] + else + let rec dragon_aux n acc = + if n = 1 then acc + else + let acc2 = remove_middle acc in + let tam = List.length acc2 in + let sub_list1 = list_copy_index acc2 0 ((tam / 2) - 1) in + let sub_list2 = list_copy_index acc2 (tam / 2) (tam - 1) in + let acc3 = + append (add_true (append (add_false acc) sub_list1)) sub_list2 + in + dragon_aux (n - 1) acc3 + in + dragon_aux n [ false ] + +(* 3 *) +let dragon_bit n : bool = + if n <= 0 then raise (Invalid_argument "dragon_bit") + else + let l = dragon n in + let x = List.nth l (n - 1) in + x \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-dragon-greatness/template.ml b/exercises/smelodesousa/F3/3-dragon-greatness/template.ml new file mode 100644 index 0000000..252469b --- /dev/null +++ b/exercises/smelodesousa/F3/3-dragon-greatness/template.ml @@ -0,0 +1,3 @@ +let dragon_size n = failwith "Unanswered" +let dragon n = failwith "Unanswered" +let dragon_bit n = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-dragon-greatness/test.ml b/exercises/smelodesousa/F3/3-dragon-greatness/test.ml new file mode 100644 index 0000000..790e74c --- /dev/null +++ b/exercises/smelodesousa/F3/3-dragon-greatness/test.ml @@ -0,0 +1,31 @@ +open Test_lib +open Report + +let test_1_with_solution = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "dragon_size" ], + test_function_1_against_solution [%ty: int -> int] "dragon_size" + ~sampler:(fun () -> Random.int 200 - 50) + ~gen:17 [ -1; 0; 1 ] ) + +let test_2_with_solution = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "dragon" ], + test_function_1_against_solution [%ty: int -> bool list] "dragon" + ~sampler:(fun () -> Random.int 25 - 5) + ~gen:17 [ -1; 0; 1; 20 ] ) + +let test_3_with_solution = + set_progress "Grading exercise 3"; + Section + ( [ Text "Exercise 3: "; Code "dragon_bit" ], + test_function_1_against_solution [%ty: int -> bool] "dragon_bit" + ~sampler:(fun () -> Random.int 1 - 5) + ~gen:0 [ -1; 0; 1; 16; 2 ] ) + +let () = + set_result @@ ast_sanity_check code_ast + @@ fun () -> + [ test_1_with_solution; test_2_with_solution; test_3_with_solution ] diff --git a/exercises/smelodesousa/F3/3-fast-exponentiation/descr.md b/exercises/smelodesousa/F3/3-fast-exponentiation/descr.md new file mode 100644 index 0000000..1fd0f7c --- /dev/null +++ b/exercises/smelodesousa/F3/3-fast-exponentiation/descr.md @@ -0,0 +1,45 @@ + + + + + + +# Introduction + +This exercise is centered around the integer exponentiation function $x^n$. The natural and direct definition of this operation is $x^n = \underbrace{x \times x \times \ldots \times x}_{n\ ti mes}$ implying $n$ multiplications. However, there is a simple alternative definition that allows us to perform the same operation more efficiently: + +
$ +x^n = { +(1, if n=0), +(x, if n=1), +(x^{\frac{n}{2}} \times x^{\frac{n}{2}}, if n\ is\ even), +(x^{\frac{n-1}{2}}\times x^{\frac{n-1}{2}}\times x, if n\ is\ odd +):} +$
+ + +This method of exponentiation is sometimes called **fast exponentiation**. + + +# Objectives + +1. Propose a recursive function in OCaml that implements this definition. + +2. What is the complexity of this algorithm (in terms of the number of multiplications made)? + + A) $\mathcal{O}(n)$ (linear)
+ B) $\mathcal{O}(n^2)$ (quadratic)
+ C) $\mathcal{O}(n * log_2(n))$
+ D) $\mathcal{O}(log_2(n))$(logarithmic)
+ E) $\mathcal{O}(1)$ (constant)
+ F) $\mathcal{O}(n * log^2(n))$

+ +**Note:** If you believe that the correct option is *`A`* then you should answer as follows: *`let answer = A`. diff --git a/exercises/smelodesousa/F3/3-fast-exponentiation/meta.json b/exercises/smelodesousa/F3/3-fast-exponentiation/meta.json new file mode 100644 index 0000000..9d2e354 --- /dev/null +++ b/exercises/smelodesousa/F3/3-fast-exponentiation/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Fast Exponentiation", + "identifier" : "3.12", + "authors" : [["Leonardo Santos", "leomendesantos@gmail.com"], ["Dário Santos","dariovfsantos@gmail.com"], ["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F3/3-tribonacci"] +} diff --git a/exercises/smelodesousa/F3/3-fast-exponentiation/prelude.ml b/exercises/smelodesousa/F3/3-fast-exponentiation/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-fast-exponentiation/prepare.ml b/exercises/smelodesousa/F3/3-fast-exponentiation/prepare.ml new file mode 100644 index 0000000..9b45dd7 --- /dev/null +++ b/exercises/smelodesousa/F3/3-fast-exponentiation/prepare.ml @@ -0,0 +1,2 @@ +type choice = + | A | B | C | D | E | F | Unanswered of string \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-fast-exponentiation/solution.ml b/exercises/smelodesousa/F3/3-fast-exponentiation/solution.ml new file mode 100644 index 0000000..ffce1e1 --- /dev/null +++ b/exercises/smelodesousa/F3/3-fast-exponentiation/solution.ml @@ -0,0 +1,9 @@ +(* 1 *) +let rec fast_exp x = function + | 0 -> 1 + | 1 -> x + | n -> let a = fast_exp x (n / 2) in + a * a * (if n mod 2 = 0 then 1 else x) + +(* 2 *) +let answer = D \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-fast-exponentiation/template.ml b/exercises/smelodesousa/F3/3-fast-exponentiation/template.ml new file mode 100644 index 0000000..1ca9344 --- /dev/null +++ b/exercises/smelodesousa/F3/3-fast-exponentiation/template.ml @@ -0,0 +1,3 @@ +let rec fast_exp x n = failwith "Replace with your solution" + +let answer = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-fast-exponentiation/test.ml b/exercises/smelodesousa/F3/3-fast-exponentiation/test.ml new file mode 100644 index 0000000..cc29a67 --- /dev/null +++ b/exercises/smelodesousa/F3/3-fast-exponentiation/test.ml @@ -0,0 +1,45 @@ +open Test_lib +open Report +open Random + +(* + check_recursion name cb + + val name: string + + Checks if function name is recursive. Check_recursion checks + if there's a function call to name inside the function name. +*) +let check_recursion name cb = + let module Error = struct exception RecursionCall end in + + find_binding code_ast name @@ fun expr -> + let contains_recursion_call = Parsetree.(function + | {pexp_desc = Pexp_apply ({pexp_desc = Pexp_ident {txt = id}}, _)} -> + if (Longident.last id) = name then raise Error.RecursionCall else [] + | _ -> []) in + try + ast_check_expr ~on_expression:contains_recursion_call expr; + [Message ([Text "The function"; Code name; Text "does not contain a recursive call"], Failure)] + with Error.RecursionCall -> cb () + + +let test_1 = Section( + [Text "Testing"; Code"fast_exp"], + check_recursion "fast_exp" @@ fun () -> test_function_2_against_solution + [%ty: int -> int -> int] + "fast_exp" + ~sampler: (fun () -> let () = Random.self_init () in (Random.int(10), Random.int(10))) + ~gen: 10 + []) + +let test_2 = Section( + [Text "Grading exercise 2"], + test_variable_against_solution + [%ty: choice] + "answer") + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [test_1; test_2] \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-hofstadter/descr.md b/exercises/smelodesousa/F3/3-hofstadter/descr.md new file mode 100644 index 0000000..b042f76 --- /dev/null +++ b/exercises/smelodesousa/F3/3-hofstadter/descr.md @@ -0,0 +1,30 @@ + + + + + +# Introduction + +In "Hofstadter, D. R. *[Gödel, Escher, Bach: An Eternal Golden Braid.](https://wikipedia.org/wiki/G%C3%B6del,_Escher,_Bach)* New York: Vintage Books, p. 137, 1989.", Hofstadter defined various numerical sequences, two of which are the female and male sequences shown: + + +
$F(n) = {(1, if n=0), (n - M(F(n-1)),if n>0):}$
+ +
+
$M(n) = {(0, if n=0), (n - F(M(n-1)),if n>0):}$
+ +# Objective + +Define the function `hfm : int -> int*int` that, for a positive `int` parameter, returns the pair $(f(n), m(n))$. + +In the case of an invalid argument or any error occurring, the exception `Failure "hfm"` is thrown. + +As an example, `hfm 7 = (5,4)`. \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-hofstadter/meta.json b/exercises/smelodesousa/F3/3-hofstadter/meta.json new file mode 100644 index 0000000..79d9b83 --- /dev/null +++ b/exercises/smelodesousa/F3/3-hofstadter/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Hofstadter's female and male number sequences", + "identifier" : "3.8", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Melo de Sousa",""], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F3/3-seq-hofstadter"] +} diff --git a/exercises/smelodesousa/F3/3-hofstadter/prelude.ml b/exercises/smelodesousa/F3/3-hofstadter/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-hofstadter/prepare.ml b/exercises/smelodesousa/F3/3-hofstadter/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-hofstadter/solution.ml b/exercises/smelodesousa/F3/3-hofstadter/solution.ml new file mode 100644 index 0000000..1f03b0e --- /dev/null +++ b/exercises/smelodesousa/F3/3-hofstadter/solution.ml @@ -0,0 +1,7 @@ +let hfm n = + let rec f n = if n <= 0 then 1 else (n - m(f(n - 1))) + and m n = if n <= 0 then 0 else (n - f(m(n - 1))) in + if (n < 0) + then raise (Failure "hfm") + else + (f n, m n) \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-hofstadter/template.ml b/exercises/smelodesousa/F3/3-hofstadter/template.ml new file mode 100644 index 0000000..cfa29ca --- /dev/null +++ b/exercises/smelodesousa/F3/3-hofstadter/template.ml @@ -0,0 +1,2 @@ +let hfm n = + failwith "Replace with your solution" diff --git a/exercises/smelodesousa/F3/3-hofstadter/test.ml b/exercises/smelodesousa/F3/3-hofstadter/test.ml new file mode 100644 index 0000000..d2c9455 --- /dev/null +++ b/exercises/smelodesousa/F3/3-hofstadter/test.ml @@ -0,0 +1,20 @@ +open Test_lib +open Report + +let factor_sampler () = + let () = Random.self_init () in + ((Random.int 50) - 5) + +let hfmS = Section ( + [Text "Testing hfm function"], + test_function_1_against_solution + [%ty: int -> (int * int) ] + "hfm" + ~sampler: factor_sampler + ~gen: 8 + [(-100); 0] ) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [hfmS] diff --git a/exercises/smelodesousa/F3/3-how-many-mountains/N4.png b/exercises/smelodesousa/F3/3-how-many-mountains/N4.png new file mode 100644 index 0000000000000000000000000000000000000000..30ec1976dfb974e5d5c299c97b816071ba3ce9c2 GIT binary patch literal 49896 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
Q{r2#yRz-aCBSz92}Y#LC`7BZl5sx7 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

&>=YT0^P|iVif9kasE4<&fk0h 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 e*!R=`!2bfy4n68&(q!)d0000 +MathJax = { + loader: {load: ['input/asciimath', 'output/chtml']}, + asciimath: { + delimiters: [['$','$'], ['`','`']] + } +} + + + + + +# Introduction + +Here we will consider a classic combinatorial (counting) problem. + +Given a value $n$, imagine a mountain landscape inscribed in a grid $(0,0)$ through $(2n,0)$. + +In our scenario, a landscape consists exclusively of mountains whose slope is a diagonal _one by one_ ascending or descending, starting at $(0,0)$ and ends at $(2n,0)$. + +Mountain profiles that descend below the $x$-axis are also not considered. + +The unsolved problem is: given $n$ (which defines the landscape size) and the number $k$ of peaks ($k$ always less than or equal to $n$), how many valid mountain profiles are there? + +For example, for $n=4$ and $k=3$, the answer is $6$. Graphically we can visualize the solutions as follows: + +![](https://i.imgur.com/lIZsgHT.png) + + +We also give two limit values for $N=4$, which are $k=1$ and $k=4$ + +![](https://i.imgur.com/NwVI8wo.png) + + +We also assume that $1\leq n \leq 30$ and $1\leq k \leq n$. + +# Objective + +Define a function `mountains: int -> int -> int option` that calculates precisely the number of possible profiles. Therefore, the result of `mountains 4 3` is `Some 6`. When the rules about `n` and `k` are not satisfied, or for some reason the value cannot be calculated, the function returns `None`. Thus, the result `mountain 3 4` is `None`. + +Hint: consider the initial cases ($n=1$ and $k=1$, followed by $n=2$ and $k=1$ or $k=2$, etc.) and check if a pattern emerges as you calculate the next cases. \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-how-many-mountains/meta.json b/exercises/smelodesousa/F3/3-how-many-mountains/meta.json new file mode 100644 index 0000000..6aaf28f --- /dev/null +++ b/exercises/smelodesousa/F3/3-how-many-mountains/meta.json @@ -0,0 +1,13 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 3, + "title": "How many mountains?", + "identifier": "3.20", + "authors": [ + ["Dário Santos", "dariovfsantos@gmail.com"], + ["Gonçalo Domingos", "goncalogdomingos@gmail.com"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F3/3-collatz-hailstones"] +} diff --git a/exercises/smelodesousa/F3/3-how-many-mountains/prelude.ml b/exercises/smelodesousa/F3/3-how-many-mountains/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-how-many-mountains/prepare.ml b/exercises/smelodesousa/F3/3-how-many-mountains/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-how-many-mountains/solution.ml b/exercises/smelodesousa/F3/3-how-many-mountains/solution.ml new file mode 100644 index 0000000..8e84bed --- /dev/null +++ b/exercises/smelodesousa/F3/3-how-many-mountains/solution.ml @@ -0,0 +1,22 @@ +(* let rec comb n k = + if k < 0 || n < 0 then 0 + else if k = 0 || k = n then 1 + else (comb (n-1) (k-1)) + (comb (n-1) k) + + let narayana n k = + ((comb n k) * (comb n (k-1))) / n + + let mountains n k = + if k > n || k < 1 then None + else Some (narayana n k) *) + +(* Another possible solution *) +let mountains n k = + if k < 1 || k > n then None + else + let rec aux_fun n k res = + if k < 0 || n < 0 then res + else if k = 0 || k = n then res + 1 + else aux_fun (n - 1) (k - 1) (aux_fun (n - 1) k res) + in + Some (aux_fun n k 0 * aux_fun n (k - 1) 0 / n) diff --git a/exercises/smelodesousa/F3/3-how-many-mountains/template.ml b/exercises/smelodesousa/F3/3-how-many-mountains/template.ml new file mode 100644 index 0000000..1a8105c --- /dev/null +++ b/exercises/smelodesousa/F3/3-how-many-mountains/template.ml @@ -0,0 +1 @@ +let mountains n k = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-how-many-mountains/test.ml b/exercises/smelodesousa/F3/3-how-many-mountains/test.ml new file mode 100644 index 0000000..acc5839 --- /dev/null +++ b/exercises/smelodesousa/F3/3-how-many-mountains/test.ml @@ -0,0 +1,19 @@ +open Test_lib +open Report +open List +open Random +open Solution + +let mountains_sampler () = (Random.int 15, Random.int 15) + +let test_mountains = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "mountains" ], + test_function_2_against_solution [%ty: int -> int -> int option] + "mountains" + ~sampler:(fun () -> mountains_sampler ()) + ~gen:15 + [ (4, 3); (1, 0); (1, 1); (30, 30) ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ test_mountains ] diff --git a/exercises/smelodesousa/F3/3-manhattan-distance/descr.md b/exercises/smelodesousa/F3/3-manhattan-distance/descr.md new file mode 100644 index 0000000..d086eba --- /dev/null +++ b/exercises/smelodesousa/F3/3-manhattan-distance/descr.md @@ -0,0 +1,13 @@ +# Introduction + +Consider the problem of optimizing taxi routes in modern cities like Manhattan, for example. Consider the following map where the roads are grayed out. Also consider that the buildings in the city, which are represented by the white squares, have dimension one. + +![](https://i.imgur.com/A4UYbex.png) + +What is the shortest taxi distance between the lower left point and the upper right point? + +If we could fly, the Euclidean distance would be the solution (the green route). For the distance traveled by car, however, the calculation must be different. Therefore, what is the length of the red route? Or the blue route? What about the yellow route? + +# Goals + +Based on the answer to these questions, propose the implementation in OCaml of the function `manhattan_distance : int -> int -> int -> int -> int` such that `manhattan_distance x y a b` calculates the Manhattan distance between the point **(x,y)** and the point **(a,b)**. diff --git a/exercises/smelodesousa/F3/3-manhattan-distance/meta.json b/exercises/smelodesousa/F3/3-manhattan-distance/meta.json new file mode 100644 index 0000000..48d3c55 --- /dev/null +++ b/exercises/smelodesousa/F3/3-manhattan-distance/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Manhattan distance", + "identifier" : "3.2", + "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F3/3-simple-math"] +} diff --git a/exercises/smelodesousa/F3/3-manhattan-distance/prelude.ml b/exercises/smelodesousa/F3/3-manhattan-distance/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-manhattan-distance/prepare.ml b/exercises/smelodesousa/F3/3-manhattan-distance/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-manhattan-distance/solution.ml b/exercises/smelodesousa/F3/3-manhattan-distance/solution.ml new file mode 100644 index 0000000..a0e01b8 --- /dev/null +++ b/exercises/smelodesousa/F3/3-manhattan-distance/solution.ml @@ -0,0 +1,2 @@ +let manhattan_distance x y a b = + (abs (x - a)) + (abs (y - b)) \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-manhattan-distance/template.ml b/exercises/smelodesousa/F3/3-manhattan-distance/template.ml new file mode 100644 index 0000000..af28625 --- /dev/null +++ b/exercises/smelodesousa/F3/3-manhattan-distance/template.ml @@ -0,0 +1 @@ +let manhattan_distance x y a b = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-manhattan-distance/test.ml b/exercises/smelodesousa/F3/3-manhattan-distance/test.ml new file mode 100644 index 0000000..0eade79 --- /dev/null +++ b/exercises/smelodesousa/F3/3-manhattan-distance/test.ml @@ -0,0 +1,29 @@ +open Test_lib +open Report + + +let sampler_manhattan () = + let x = Random.int 200 in + let y = Random.int 200 in + + (* 25% de chance de x = a *) + let a = if (Random.int 100) < 25 then x else Random.int 200 in + (* 25% de chance de y = b *) + let b = if (Random.int 100) < 25 then y else Random.int 200 in + x,y,a,b + +let test_valid_with_solution = + Section([Text "Valid tests"; Code("manhattan_distance")], + test_function_4_against_solution + [%ty: int -> int -> int -> int -> int] + "manhattan_distance" + ~sampler: (fun () -> sampler_manhattan ()) + ~gen:39 + [(0,0,0,0)] + ) + + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [test_valid_with_solution] \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-mccarthy-91-function/descr.md b/exercises/smelodesousa/F3/3-mccarthy-91-function/descr.md new file mode 100644 index 0000000..1ab2c1b --- /dev/null +++ b/exercises/smelodesousa/F3/3-mccarthy-91-function/descr.md @@ -0,0 +1,30 @@ + + + + + +# Introduction + +In the 70s, John McCarthy, one of the minds behind artificial intelligence, in partnership with Zohar Manna and Amir Pnueli, tried to define a recursive function that could be used as a test case for various algorithms. + +This function is the function $m : \mathbb{N} \to \mathbb{N}$ and can be defined in the following manner: + +

$m(n) = {(n-10, if n>100), (m(m(n+11)), if n\leq 100):}$
+ +# Goals + +1. Define the function `mccarthy : int -> int` that implements this recursive definition. As an example, `maccarthy 200 = 190` and `mccarthy 24 = 91` + +2. Write an implementation of the function `f91` using the following definition: +
+
$f91(n) ={(n-10, if n>100\), (91, if n\leq 100):}$

+ +3. Define a recursive function `mccarthy_is_f91 : int -> int -> bool` that verifies if, in the integer interval defined by the two `int` parameters, the functions return the same results, for example `mccarthy_is_f91 70 120 = mccarthy_is_f91 120 70 = true`. diff --git a/exercises/smelodesousa/F3/3-mccarthy-91-function/meta.json b/exercises/smelodesousa/F3/3-mccarthy-91-function/meta.json new file mode 100644 index 0000000..62e285d --- /dev/null +++ b/exercises/smelodesousa/F3/3-mccarthy-91-function/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "McCarthy 91 function", + "identifier" : "3.15", + "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["mooc/week2/seq2/ex2"] +} diff --git a/exercises/smelodesousa/F3/3-mccarthy-91-function/prelude.ml b/exercises/smelodesousa/F3/3-mccarthy-91-function/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-mccarthy-91-function/prepare.ml b/exercises/smelodesousa/F3/3-mccarthy-91-function/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-mccarthy-91-function/solution.ml b/exercises/smelodesousa/F3/3-mccarthy-91-function/solution.ml new file mode 100644 index 0000000..bac87fc --- /dev/null +++ b/exercises/smelodesousa/F3/3-mccarthy-91-function/solution.ml @@ -0,0 +1,8 @@ +let rec mccarthy n = + if n > 100 then n - 10 else mccarthy (mccarthy (n + 11)) + +let f91 n = if n > 100 then n - 10 else 91 + +let rec mccarthy_is_f91 i f = + if i > f then true + else (mccarthy i = f91 i) && (mccarthy_is_f91 (i+1) f) diff --git a/exercises/smelodesousa/F3/3-mccarthy-91-function/template.ml b/exercises/smelodesousa/F3/3-mccarthy-91-function/template.ml new file mode 100644 index 0000000..0c92a65 --- /dev/null +++ b/exercises/smelodesousa/F3/3-mccarthy-91-function/template.ml @@ -0,0 +1,5 @@ +let rec mccarthy n = failwith "Replace with your solution" + +let f91 n = failwith "Replace with your solution" + +let rec mccarthy_is_f91 i f = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-mccarthy-91-function/test.ml b/exercises/smelodesousa/F3/3-mccarthy-91-function/test.ml new file mode 100644 index 0000000..fb394ac --- /dev/null +++ b/exercises/smelodesousa/F3/3-mccarthy-91-function/test.ml @@ -0,0 +1,37 @@ +open Test_lib +open Report + +let test_a_with_solution = + Section([Text "Testing"; Code("mccarthy")], + test_function_1_against_solution + [%ty: int -> int] + "mccarthy" + ~sampler: (fun () -> (Random.int 200)) + ~gen:19 + [200] + ) + +let test_b_with_solution = + Section([Text "Testing"; Code("f91")], + test_function_1_against_solution + [%ty: int -> int] + "f91" + ~sampler: (fun () -> (Random.int 200)) + ~gen:17 + [99; 100; 101] + ) + +let test_c_with_solution = + Section([Text "Testing"; Code("mccarthy_is_f91")], + test_function_2_against_solution + [%ty: int -> int -> bool] + "mccarthy_is_f91" + ~sampler: (fun () -> let i = Random.int 100 in (i, (Random.int 100) + i)) + ~gen:17 + [(70, 120)] + ) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [test_a_with_solution; test_b_with_solution; test_c_with_solution] \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-pi/descr.md b/exercises/smelodesousa/F3/3-pi/descr.md new file mode 100644 index 0000000..f1a76b9 --- /dev/null +++ b/exercises/smelodesousa/F3/3-pi/descr.md @@ -0,0 +1,43 @@ + + + + + +# Introduction + +_Wallis_'s formula provides an approximation of $\Pi$ defined in the following manner: + +
$\frac{\Pi}{2} = \frac{2\times 2}{1 \times 3} \times \frac{4\times + 4}{3 \times 5} \times \frac{6\times 6}{5 \times 7} \cdots$
+ +- Provide a recursive definition of this approximation by creating a function $f$ that receives an index $k\geq 1$ so that if $k=1$, the function calculates the following formula: + +
$ +2 \times \frac {2\times 2} {1 \times 3} +$
+ +   , if $k=2$, then $f(2)$ determines + +
$ +2 \times +\frac{2\times 2}{1 \times 3} \times \frac{4\times + 4}{3 \times 5} +$
+ +   , etc. + +# Objective + +Given the definition above, implement this function in OCaml in the form of the function `approximate_pi : int -> float`, that given the index $k$, returns the result of $f(k)$. + +Consider that $0 < k \leq 10000$. If it is not possible to determine $f(k)$, `approximate_pi` throws the exception `Failure "approximate_pi"`. + +Example: `approximate_pi 5 = 3.00217595455690622` \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-pi/meta.json b/exercises/smelodesousa/F3/3-pi/meta.json new file mode 100644 index 0000000..af7e6fb --- /dev/null +++ b/exercises/smelodesousa/F3/3-pi/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Approximate π", + "identifier": "3.5", + "authors": [ + ["Rui Barata", "rui.barata@ubi.pt"], + ["Simão Melo de Sousa", ""] + ], + "backward_exercises": ["smelodesousa/F3/3-reach-infinity"] +} diff --git a/exercises/smelodesousa/F3/3-pi/prelude.ml b/exercises/smelodesousa/F3/3-pi/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-pi/prepare.ml b/exercises/smelodesousa/F3/3-pi/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-pi/solution.ml b/exercises/smelodesousa/F3/3-pi/solution.ml new file mode 100644 index 0000000..3f1e013 --- /dev/null +++ b/exercises/smelodesousa/F3/3-pi/solution.ml @@ -0,0 +1,21 @@ +let rec approximate_pi k = + let n = float_of_int k in + match n with + | invalid when n <= 0. || n > 10000. -> raise (Failure "approximate_pi") + | 1. -> 8. /. 3. + | _ -> + approximate_pi (k - 1) + *. (n *. 2. *. (n *. 2.) /. (((n *. 2.) -. 1.) *. ((n *. 2.) +. 1.))) + +(* Original version: + let rec approximate_pi k = + try + let f = float_of_int k in + if k < 1 + then raise (Failure "approximate_pi") + else + if k = 1 + then 2. *. (((f*.2.)*.(f*.2.))/.(((f*.2.)-.1.)*.((f*.2.)+.1.))) + else (((f*.2.)*.(f*.2.))/.(((f*.2.)-.1.)*.((f*.2.)+.1.))) *. (approximate_pi (k-1)) + with + Stack_overflow -> raise (Failure "approximate_pi") *) diff --git a/exercises/smelodesousa/F3/3-pi/template.ml b/exercises/smelodesousa/F3/3-pi/template.ml new file mode 100644 index 0000000..4d92aee --- /dev/null +++ b/exercises/smelodesousa/F3/3-pi/template.ml @@ -0,0 +1 @@ +let rec approximate_pi k = failwith "Unanswered" diff --git a/exercises/smelodesousa/F3/3-pi/test.ml b/exercises/smelodesousa/F3/3-pi/test.ml new file mode 100644 index 0000000..e5c4f4f --- /dev/null +++ b/exercises/smelodesousa/F3/3-pi/test.ml @@ -0,0 +1,42 @@ +open Test_lib +open Report + +let factor_sampler () = + let () = Random.self_init () in + Random.int 1000 + +let check_recursion name cb = + let module Error = struct + exception RecursionCall + end in + find_binding code_ast name @@ fun expr -> + let contains_recursion_call = + Parsetree.( + function + | { pexp_desc = Pexp_apply ({ pexp_desc = Pexp_ident { txt = id } }, _) } + -> + if Longident.last id = name then raise Error.RecursionCall else [] + | _ -> []) + in + try + ast_check_expr ~on_expression:contains_recursion_call expr; + [ + Message + ( [ + Text "The function"; + Code name; + Text "does not contain a recursive call"; + ], + Failure ); + ] + with Error.RecursionCall -> cb () + +let aproximateS = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "approximate_pi" ], + check_recursion "approximate_pi" @@ fun () -> + test_function_1_against_solution [%ty: int -> float] "approximate_pi" + ~sampler:factor_sampler ~gen:8 [ -100; 0; 10001 ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ aproximateS ] diff --git a/exercises/smelodesousa/F3/3-reach-infinity/descr.md b/exercises/smelodesousa/F3/3-reach-infinity/descr.md new file mode 100644 index 0000000..120e45a --- /dev/null +++ b/exercises/smelodesousa/F3/3-reach-infinity/descr.md @@ -0,0 +1,37 @@ + + + + + + +# Introduction + +Wilhelm Friedrich Ackermann, a German mathematician, contributed to the study of computable functions by presenting a function with unique properties: a total computable function that escaped, in the time of its discovery, the classification that was thought to be that of functions with these characteristics. + +Partly because the function... has an explosive behavior! + +The initially proposed function had 3 parameters, but the one that was recorded for history includes two parameters and is defined as follows: + +
+$A(0,n) = n + 1$
+$A(m+1,0) = A(m,1)$
+$A(m+1,n+1) = A(m,A(m+1,n))$ +
+ +With paper and pencil, determine the result of A(4,4). + +If you read this sentence, know that A(4,2) results in a number with more than 19,000 digits ($2^{2^{2^{2^{2}}}}-3$) and that the previous paragraph is the result of a bad joke. It is not expected that you solve it! + +# Goal + +Define the recursive function `ackermann int -> int -> int`, which replicates this function. + +For example, `ackermann 3 4 = 125`. \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-reach-infinity/meta.json b/exercises/smelodesousa/F3/3-reach-infinity/meta.json new file mode 100644 index 0000000..dbb029a --- /dev/null +++ b/exercises/smelodesousa/F3/3-reach-infinity/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Reach infinity", + "identifier" : "3.4", + "authors" : [["Dário Santos","dariovfsantos@gmail.com"], ["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F3/3-a-historic-algorithm"] +} diff --git a/exercises/smelodesousa/F3/3-reach-infinity/prelude.ml b/exercises/smelodesousa/F3/3-reach-infinity/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-reach-infinity/prepare.ml b/exercises/smelodesousa/F3/3-reach-infinity/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-reach-infinity/solution.ml b/exercises/smelodesousa/F3/3-reach-infinity/solution.ml new file mode 100644 index 0000000..eb496ed --- /dev/null +++ b/exercises/smelodesousa/F3/3-reach-infinity/solution.ml @@ -0,0 +1,10 @@ +let rec ackermann m n = + if n < 0 || m < 0 then + raise(Invalid_argument "numbers cannot be negative") + else + if m=0 then (n+1) + else if n=0 then + (ackermann (m-1) 1) + else + (ackermann (m-1) (ackermann m (n-1))) + diff --git a/exercises/smelodesousa/F3/3-reach-infinity/template.ml b/exercises/smelodesousa/F3/3-reach-infinity/template.ml new file mode 100644 index 0000000..6899336 --- /dev/null +++ b/exercises/smelodesousa/F3/3-reach-infinity/template.ml @@ -0,0 +1 @@ +let rec ackermann m n = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-reach-infinity/test.ml b/exercises/smelodesousa/F3/3-reach-infinity/test.ml new file mode 100644 index 0000000..04d5453 --- /dev/null +++ b/exercises/smelodesousa/F3/3-reach-infinity/test.ml @@ -0,0 +1,39 @@ +open Test_lib +open Report +open Random + +(* + check_recursion name cb + + val name: string + + Checks if function name is recursive. Check_recursion checks + if there's a function call to name inside the function name. +*) +let check_recursion name cb = + let module Error = struct exception RecursionCall end in + + find_binding code_ast name @@ fun expr -> + let contains_recursion_call = Parsetree.(function + | {pexp_desc = Pexp_apply ({pexp_desc = Pexp_ident {txt = id}}, _)} -> + if (Longident.last id) = name then raise Error.RecursionCall else [] + | _ -> []) in + try + ast_check_expr ~on_expression:contains_recursion_call expr; + [Message ([Text "The function"; Code name; Text "does not contain a recursive call"], Failure)] + with Error.RecursionCall -> cb () + + +let test_ackermann = Section ( + [Text "Testing function"; Code "ackermann"], + check_recursion "ackermann" @@ fun () -> test_function_2_against_solution + [%ty: int -> int -> int] + "ackermann" + ~sampler: (fun ()-> (let () = Random.self_init () in (Random.int(3),Random.int(10)))) + ~gen: 10 + []) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [test_ackermann] \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-seq-hofstadter/descr.md b/exercises/smelodesousa/F3/3-seq-hofstadter/descr.md new file mode 100644 index 0000000..b9b6aeb --- /dev/null +++ b/exercises/smelodesousa/F3/3-seq-hofstadter/descr.md @@ -0,0 +1,32 @@ + + + + + +# Introduction + +Consider $r$ and $s$ to be two natural positive integers, where $s\geq 2$ and $r$Q_{r,s}(n) = +{(1, if\ 1 \leq n \leq s), +(Q_{r,s} (n - Q_{r,s} (n-r)) + Q_{r,s} (n - Q_{r,s} (n - s)), if\ n>s +):}$ + +where $n$ is a positive integer. + +

Although, this family of values suffers from some irregularities. In particular, when the values $Q_{r,s}(n)$ are not defined (i.e. when $n - Q_{r,s}(n-r) < 1$ or $n - Q_{r,s}(n-s) < 1$) or whenever any other established condition about $r$ and $s$ isn't respected, we say that the sequence dies.

+ +# Objective + +

Implement the function hhq : int -> int -> int -> int so that hhq r s n determines the value of $Q_{r,s}(n)$.

+ +In case of an invalid argument or an irregular situation, the exception `Failure "hhq"` is thrown. +Thus, `hhq 1 4 12 = 7`. \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-seq-hofstadter/meta.json b/exercises/smelodesousa/F3/3-seq-hofstadter/meta.json new file mode 100644 index 0000000..470be1b --- /dev/null +++ b/exercises/smelodesousa/F3/3-seq-hofstadter/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "The Hofstadter-Huber Qr,s(n) sequences", + "identifier": "3.7", + "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], + "backward_exercises": ["smelodesousa/F3/3-digits"] +} diff --git a/exercises/smelodesousa/F3/3-seq-hofstadter/prelude.ml b/exercises/smelodesousa/F3/3-seq-hofstadter/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-seq-hofstadter/prepare.ml b/exercises/smelodesousa/F3/3-seq-hofstadter/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-seq-hofstadter/solution.ml b/exercises/smelodesousa/F3/3-seq-hofstadter/solution.ml new file mode 100644 index 0000000..da3e67b --- /dev/null +++ b/exercises/smelodesousa/F3/3-seq-hofstadter/solution.ml @@ -0,0 +1,14 @@ +let rec hhq r s n = + if s < 2 || r >= s then raise (Failure "hhq") + else if 1 <= n && n <= s then 1 + else if n > s then + hhq r s (n - hhq r s (n - r)) + hhq r s (n - hhq r s (n - s)) + else raise (Failure "hhq") + +(* Original version: + let rec hhq r s n = + begin + if n <= 0 || r>=s || s<2 then raise(Failure "hhq"); + if n <= s && n >= 1 then 1 + else (hhq r s (n - (hhq r s (n-r)))) + (hhq r s (n - (hhq r s (n-s)))) + end *) diff --git a/exercises/smelodesousa/F3/3-seq-hofstadter/template.ml b/exercises/smelodesousa/F3/3-seq-hofstadter/template.ml new file mode 100644 index 0000000..80b6181 --- /dev/null +++ b/exercises/smelodesousa/F3/3-seq-hofstadter/template.ml @@ -0,0 +1 @@ +let rec hhq r s n = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-seq-hofstadter/test.ml b/exercises/smelodesousa/F3/3-seq-hofstadter/test.ml new file mode 100644 index 0000000..929c38a --- /dev/null +++ b/exercises/smelodesousa/F3/3-seq-hofstadter/test.ml @@ -0,0 +1,25 @@ +open Test_lib +open Report +open List +open Random + +let hhqR = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "hhq" ], + test_function_3_against_solution [%ty: int -> int -> int -> int] "hhq" + ~sampler:(fun () -> + let () = Random.self_init () in + (Random.int 3 + 1, Random.int 7 + 1, Random.int 14 + 1)) + ~gen:10 + [ + (2, 5, 10); + (1, 4, 12); + (2, 2, 20); + (1, 4, 15); + (2, 4, 17); + (0, 1, 10); + (5, 2, 10); + ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ hhqR ] diff --git a/exercises/smelodesousa/F3/3-simple-math/descr.md b/exercises/smelodesousa/F3/3-simple-math/descr.md new file mode 100644 index 0000000..03ccbb5 --- /dev/null +++ b/exercises/smelodesousa/F3/3-simple-math/descr.md @@ -0,0 +1,22 @@ + + + + + +1. Define a function `distance : (float * float) -> (float * float) -> float` that calculates the distance between two given points in parameters. + +2. Define a function `area: float -> float` that calculates the area of a circle whose radius is given as a parameter. + +3. Define a function `sin2x : float -> float` that, given a floating parameter x, calculates the following expression: + +
+$\frac{2 \times tanx}{1+tan^2x}$ +
diff --git a/exercises/smelodesousa/F3/3-simple-math/meta.json b/exercises/smelodesousa/F3/3-simple-math/meta.json new file mode 100644 index 0000000..5c00586 --- /dev/null +++ b/exercises/smelodesousa/F3/3-simple-math/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Simple math", + "identifier": "3.1", + "authors": [ + ["Rui Barata", "rui.barata@ubi.pt"], + ["Simão Melo de Sousa", ""] + ], + "backward_exercises": ["mooc/week2/seq1/ex1"] +} diff --git a/exercises/smelodesousa/F3/3-simple-math/prelude.ml b/exercises/smelodesousa/F3/3-simple-math/prelude.ml new file mode 100644 index 0000000..a4a58cb --- /dev/null +++ b/exercises/smelodesousa/F3/3-simple-math/prelude.ml @@ -0,0 +1,3 @@ +let pi = acos (-1.0) + +(* equivalente a Float.pi *) \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-simple-math/prepare.ml b/exercises/smelodesousa/F3/3-simple-math/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-simple-math/solution.ml b/exercises/smelodesousa/F3/3-simple-math/solution.ml new file mode 100644 index 0000000..c7336e2 --- /dev/null +++ b/exercises/smelodesousa/F3/3-simple-math/solution.ml @@ -0,0 +1,10 @@ +let square x = x *. x + +let distance p1 p2 = + let x1, y1 = p1 and x2, y2 = p2 in + sqrt (square (x2 -. x1) +. square (y2 -. y1)) + +let area r = pi *. r *. r +(* pi *. square r *) + +let sin2x x = 2.0 *. tan x /. (1.0 +. square (tan x)) diff --git a/exercises/smelodesousa/F3/3-simple-math/template.ml b/exercises/smelodesousa/F3/3-simple-math/template.ml new file mode 100644 index 0000000..ae3d685 --- /dev/null +++ b/exercises/smelodesousa/F3/3-simple-math/template.ml @@ -0,0 +1,3 @@ +let distance p1 p2 = failwith "Unanswered" +let area r = failwith "Unanswered" +let sin2x x = failwith "Unanswered" diff --git a/exercises/smelodesousa/F3/3-simple-math/test.ml b/exercises/smelodesousa/F3/3-simple-math/test.ml new file mode 100644 index 0000000..4ef37b0 --- /dev/null +++ b/exercises/smelodesousa/F3/3-simple-math/test.ml @@ -0,0 +1,67 @@ +open Test_lib +open Report +open Learnocaml_report + +let samplePoints () = + let () = Random.self_init () in + let x1 = if Random.int 2 = 1 then -.Random.float 50.0 else Random.float 50.0 + and y1 = if Random.int 2 = 1 then -.Random.float 50.0 else Random.float 50.0 + and x2 = if Random.int 2 = 1 then -.Random.float 50.0 else Random.float 50.0 + and y2 = + if Random.int 2 = 1 then -.Random.float 50.0 else Random.float 50.0 + in + ((x1, y1), (x2, y2)) + +let testdistance = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "distance" ], + test_function_2_against_solution + [%ty: float * float -> float * float -> float] "distance" + ~sampler:samplePoints ~gen:8 + [ ((0.0, 0.0), (2.0, 2.0)); ((2.0, 2.0), (2.0, 2.0)) ] ) + +let epsilon = 1.e-10 +let ( =. ) a b = (if a > b then a -. b else b -. a) < epsilon + +let areaS = + set_progress "Grading exercise 2"; + let test = ref [] in + let t = + test_function_1_against_solution [%ty: float -> float] "area" + ~sampler:(fun () -> + let () = Random.self_init () in + Random.float 35. +. 5.) + ~gen:8 + ~after:(fun a (b, c, d) (e, f, g) -> + if b =. e then + test := + Message + ([ Text "Correct value"; Code (string_of_float b) ], Success 1) + :: Message + ( [ Text "Computing area "; Code (string_of_float a) ], + Informative ) + :: !test + else + test := + Message ([ Text "Wrong value"; Code (string_of_float b) ], Failure) + :: Message + ( [ Text "Computing area "; Code (string_of_float a) ], + Informative ) + :: !test; + !test) + [ 0.; 100. ] + in + Section ([ Text "Exercise 2: "; Code "area" ], List.hd t :: List.rev !test) + +let sin2xS = + set_progress "Grading exercise 3"; + Section + ( [ Text "Exercise 3: "; Code "sin2x" ], + test_function_1_against_solution [%ty: float -> float] "sin2x" + ~sampler:(fun () -> sample_float () +. 5.) + ~gen:8 [ 0.; 100. ] ) + +let () = + set_result @@ ast_sanity_check code_ast + @@ fun () -> [ testdistance; areaS; sin2xS ] diff --git a/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/descr.md b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/descr.md new file mode 100644 index 0000000..c17cd96 --- /dev/null +++ b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/descr.md @@ -0,0 +1,43 @@ + + + + + +# Introduction + +Consider the following pattern composed of empty spaces and asterisks: + +```pseudocode +* +* * + * +* * * * + * + * * + * +* * * * * * * * + * + * * + * + * * * * + * + * * + * +``` + + +# Objectives + +Discover the construction rules that create the pattern and implement a recursive function `fractals : int -> unit` that, given an integer $n$, which is a power of $2$ contained in the interval $[1\ldots 100]$, produces this type of pattern in the `std_out` where $n$ is the number of asterisks the longest line will have. + +For instance, `fractals 8` prints the pattern shown above. + +In the case of an invalid argument, the exception `Invalid_argument "fractals"` is thrown. \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/meta.json b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/meta.json new file mode 100644 index 0000000..9145124 --- /dev/null +++ b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/meta.json @@ -0,0 +1,13 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Stars in the form of textual fractals", + "identifier": "3.18", + "authors": [ + ["Dário Santos", "dariovfsantos@gmail.com"], + ["Rui Barata", "rui.barata@ubi.pt"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["hferee/6_sierpinsky_ascii"] +} diff --git a/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/prelude.ml b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/prepare.ml b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/solution.ml b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/solution.ml new file mode 100644 index 0000000..efa47b3 --- /dev/null +++ b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/solution.ml @@ -0,0 +1,57 @@ +let fractals n = + let rec isPow2 = function + | 1 -> true + | n -> if n mod 2 = 0 then isPow2 (n / 2) else false + in + let rec print_stars = function + | n -> + if n > 1 then ( + print_string "* "; + print_stars (n - 1)) + else print_endline "*" + in + let rec print_spaces = function + | n -> + if n > 0 then ( + print_string " "; + print_spaces (n - 1)) + else print_string "" + in + let rec aux n spaces = + if n > 0 then ( + aux (n / 2) spaces; + print_spaces spaces; + print_stars n; + aux (n / 2) (spaces + (n / 2))) + in + match n with + | invalid when n < 1 || n > 100 || not (isPow2 n) -> + raise (Invalid_argument "fractals") + | _ -> aux n 0 + +(* NOTE: On the original solution (below) there's an empty space before new lines ("* * \n"). + On the proposed solution above, that's not the case. ("* *\n") *) + +(* Original version: + let rec valid n = + match n with + | 1 -> true + | _ -> match (n mod 2) with 0 -> valid (n/2) | _ -> false + + let rec fractals n = + let rec fractals_aux left length = + + if length = 0 + then () + else + begin + + fractals_aux left (length/2); + + for i=0 to (left-1) do Printf.printf " " done; + for i=0 to (length-1) do Printf.printf "* " done; + Printf.printf "\n"; + + fractals_aux (left + (length/2)) (length/2) + end in + if (valid n) then fractals_aux 0 n else raise (Invalid_argument "fractals") *) diff --git a/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/template.ml b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/template.ml new file mode 100644 index 0000000..946541f --- /dev/null +++ b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/template.ml @@ -0,0 +1 @@ +let rec fractals n = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/test.ml b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/test.ml new file mode 100644 index 0000000..1999851 --- /dev/null +++ b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/test.ml @@ -0,0 +1,16 @@ +open Test_lib +open Report + +let test_fractals = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "fractals" ], + test_function_1_against_solution [%ty: int -> unit] "fractals" + ~test_stdout:io_test_equals + ~sampler:(fun () -> + let () = Random.self_init () in + int_of_float (2. ** float_of_int (Random.int 7))) + ~gen:5 + [ 8; 0; -1; -2; 3; 100; 101; 128 ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ test_fractals ] diff --git a/exercises/smelodesousa/F3/3-sums/descr.md b/exercises/smelodesousa/F3/3-sums/descr.md new file mode 100644 index 0000000..d660d83 --- /dev/null +++ b/exercises/smelodesousa/F3/3-sums/descr.md @@ -0,0 +1,21 @@ + + + + + +Consider the following mathematical expression: + +
$underset(i=0)(sum^n) 3^i$
+ +
    +
  1. Define the function sum3 : int -> int such that sum3 n returns the value $underset(i=0)(sum^n) 3^i$.
  2. +
  3. Provide a tail recursive version sum3_tr : int -> int -> int.
  4. +
\ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-sums/meta.json b/exercises/smelodesousa/F3/3-sums/meta.json new file mode 100644 index 0000000..8c6f4ff --- /dev/null +++ b/exercises/smelodesousa/F3/3-sums/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Sums", + "identifier" : "3.9", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"],["Simão Melo de Sousa",""]], + "backward_exercises": ["smelodesousa/F3/3-hofstadter"] +} diff --git a/exercises/smelodesousa/F3/3-sums/prelude.ml b/exercises/smelodesousa/F3/3-sums/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-sums/prepare.ml b/exercises/smelodesousa/F3/3-sums/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-sums/solution.ml b/exercises/smelodesousa/F3/3-sums/solution.ml new file mode 100644 index 0000000..11a43d9 --- /dev/null +++ b/exercises/smelodesousa/F3/3-sums/solution.ml @@ -0,0 +1,21 @@ +let rec pow3 n = + if n = 0 + then 1 + else + 3 * (pow3 (n-1)) + +let rec sum3 n = + if n = 0 + then + pow3 n + else + ((pow3 n) + (sum3 (n-1))) + +let rec sum3_tr n aux = + if n = 0 + then + aux + (pow3 n) + else + sum3_tr (n-1) (aux + (pow3 n)) + + diff --git a/exercises/smelodesousa/F3/3-sums/template.ml b/exercises/smelodesousa/F3/3-sums/template.ml new file mode 100644 index 0000000..f3eeb2f --- /dev/null +++ b/exercises/smelodesousa/F3/3-sums/template.ml @@ -0,0 +1,5 @@ +let sum3 n = + failwith "Replace with your solution" + +let rec sum3_tr n aux = + failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-sums/test.ml b/exercises/smelodesousa/F3/3-sums/test.ml new file mode 100644 index 0000000..ac88b0b --- /dev/null +++ b/exercises/smelodesousa/F3/3-sums/test.ml @@ -0,0 +1,55 @@ +open Test_lib +open Report + +exception SeenLoops + +let failWith msg = + [Message ([Text msg], Failure)] + +let checkForLoops cb = + find_binding code_ast "sum3_tr" @@ fun expr -> + + let contains_loops = Parsetree.(function + | { pexp_desc = Pexp_for _ } | { pexp_desc = Pexp_while _ } -> raise SeenLoops + | _ -> []) + in + try + ast_check_expr ~on_expression:contains_loops expr; + cb () + with SeenLoops -> + failWith "Loops are not allowed in this exercise! Propose a tr recursive version" + +let int_sampler () = + let () = Random.self_init () in + (Random.int 12) + +let int_tr_sampler () = + let () = Random.self_init () in + (Random.int 12, 0) + +let sum3S = + set_progress "Correcting question 1" ; + Section ([ Text "Exercise 1: " ; Code "sum3" ], + test_function_1_against_solution + [%ty: int -> int ] + "sum3" + ~sampler: int_sampler + ~gen: 9 + [ 0 ]) + +let sum3_trS = + set_progress "Correcting question 2" ; + Section ([ Text "Exercise 2: " ; Code "sum3_tr" ], + checkForLoops @@ fun () -> test_function_2_against_solution + [%ty: int -> int -> int ] + "sum3_tr" + ~sampler: int_tr_sampler + ~gen: 9 + [ (0, 0) ]) + + + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ sum3S; sum3_trS] diff --git a/exercises/smelodesousa/F3/3-triangles/descr.md b/exercises/smelodesousa/F3/3-triangles/descr.md new file mode 100644 index 0000000..ba5327c --- /dev/null +++ b/exercises/smelodesousa/F3/3-triangles/descr.md @@ -0,0 +1,28 @@ + + + + + +# Introduction + +Consider the following sequence of equilateral triangles: + +![](https://t1.daumcdn.net/cfile/tistory/2272113A566FC05815) + +If we list the length of the sides of the triangles in this sequence, we have: + +
$1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, \cdots$
+ +# Goal + +Define a function `triangles : int -> int` which returns the nth element of this sequence (starting at index 0). + +For example, `triangles 0 = 1`, `triangles 3 = 2` and `triangles 9 = 9` . diff --git a/exercises/smelodesousa/F3/3-triangles/meta.json b/exercises/smelodesousa/F3/3-triangles/meta.json new file mode 100644 index 0000000..4d96ddf --- /dev/null +++ b/exercises/smelodesousa/F3/3-triangles/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Triangles", + "identifier" : "3.16", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"],["Simão Melo de Sousa",""]], + "backward_exercises": ["smelodesousa/F3/3-mccarthy-91-function"] +} diff --git a/exercises/smelodesousa/F3/3-triangles/prelude.ml b/exercises/smelodesousa/F3/3-triangles/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-triangles/prepare.ml b/exercises/smelodesousa/F3/3-triangles/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-triangles/solution.ml b/exercises/smelodesousa/F3/3-triangles/solution.ml new file mode 100644 index 0000000..9c31f5d --- /dev/null +++ b/exercises/smelodesousa/F3/3-triangles/solution.ml @@ -0,0 +1,4 @@ +let rec triangles n = + if n < 3 && n >= 0 + then 1 + else (triangles (n-2) + triangles (n-3)) \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-triangles/template.ml b/exercises/smelodesousa/F3/3-triangles/template.ml new file mode 100644 index 0000000..01c1d57 --- /dev/null +++ b/exercises/smelodesousa/F3/3-triangles/template.ml @@ -0,0 +1,2 @@ +let rec triangles n = + failwith "Replace with your solution" diff --git a/exercises/smelodesousa/F3/3-triangles/test.ml b/exercises/smelodesousa/F3/3-triangles/test.ml new file mode 100644 index 0000000..ddb7e1c --- /dev/null +++ b/exercises/smelodesousa/F3/3-triangles/test.ml @@ -0,0 +1,19 @@ +open Test_lib +open Report + +let samplerInts () = + let () = Random.self_init () in + Random.int 51 + +let trianglesS () = + test_function_1_against_solution + [%ty: int -> int ] + "triangles" + ~sampler: samplerInts + ~gen: 9 + [0] + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + trianglesS () diff --git a/exercises/smelodesousa/F3/3-tribonacci/descr.md b/exercises/smelodesousa/F3/3-tribonacci/descr.md new file mode 100644 index 0000000..e5b93c1 --- /dev/null +++ b/exercises/smelodesousa/F3/3-tribonacci/descr.md @@ -0,0 +1,34 @@ + + + + + +# Introduction + +Consider the following _tribonacci_ function over natural integers: + +
$"tribonacci"(n) = { +(1, if\ n=0), +(1, if\ n=1), +(1, if\ n=2), +(\Sigma_{i=1}^3 "tribonacci"\ (n-i), if\ n>2) +:}$
+ + +# Objectives + +1. Implement the recursive function `tribonacci : int -> int` that determines the tribonacci value for any given number based on the definition above. In the case of an invalid argument, the exception `Invalid_argument "tribonacci"` is thrown. + +2. Implement the iterative version `tribonacci_iter : int -> int` with loops and references to determine the tribonacci value for any given number. + +3. Implement the tail recursive function `tribonacci_tail : int -> int -> int -> int`. Assume that when `tribonacci_tail n a b c` is called, the values of a, b and c are 1. + +Example: `tribonacci_tail 6 1 1 1 = 17` \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-tribonacci/meta.json b/exercises/smelodesousa/F3/3-tribonacci/meta.json new file mode 100644 index 0000000..d2d3a3e --- /dev/null +++ b/exercises/smelodesousa/F3/3-tribonacci/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Tribonacci", + "identifier": "3.10", + "authors": [ + ["Dário Santos", "dariovfsantos@gmail.com"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F3/3-sums"] +} diff --git a/exercises/smelodesousa/F3/3-tribonacci/prelude.ml b/exercises/smelodesousa/F3/3-tribonacci/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-tribonacci/prepare.ml b/exercises/smelodesousa/F3/3-tribonacci/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F3/3-tribonacci/solution.ml b/exercises/smelodesousa/F3/3-tribonacci/solution.ml new file mode 100644 index 0000000..a5983a7 --- /dev/null +++ b/exercises/smelodesousa/F3/3-tribonacci/solution.ml @@ -0,0 +1,51 @@ +let rec tribonacci n = + match n with + | invalid when n < 0 -> raise (Invalid_argument "tribonacci") + | 0 -> 1 + | 1 -> 1 + | 2 -> 1 + | n -> tribonacci (n - 1) + tribonacci (n - 2) + tribonacci (n - 3) + +let tribonacci_iter n = + match n with + | invalid when n < 0 -> raise (Invalid_argument "tribonacci") + | 0 | 1 | 2 -> 1 + | _ -> + let a = ref 1 in + let b = ref 1 in + let c = ref 1 in + for i = 3 to n do + let aux = !a + !b + !c in + a := !b; + b := !c; + c := aux + done; + !c + +let rec tribonacci_tail n a b c = + match n with + | invalid when n < 0 -> raise (Invalid_argument "tribonacci") + | 0 | 1 | 2 -> c + | _ -> tribonacci_tail (n - 1) b c (a + b + c) + +(* Original version: + let rec tribonacci n = + if n <= 2 then 1 + else (tribonacci (n-1)) + (tribonacci (n-2)) + (tribonacci (n-3)) + + let tribonacci_iter n = + let n1 = ref 1 in + let n2 = ref 1 in + let n3 = ref 1 in + + for i=3 to n do + let tmp = !n3 in + n3 := !n1 + !n2 + !n3; + n1 := !n2; + n2 := tmp; + done; + !n3 + + let rec tribonacci_tail n a b c = + if n <= 2 then c + else (tribonacci_tail[@tailcall]) (n-1) b c (a+b+c) *) diff --git a/exercises/smelodesousa/F3/3-tribonacci/template.ml b/exercises/smelodesousa/F3/3-tribonacci/template.ml new file mode 100644 index 0000000..b10d85c --- /dev/null +++ b/exercises/smelodesousa/F3/3-tribonacci/template.ml @@ -0,0 +1,3 @@ +let rec tribonacci n = failwith "Unanswered" +let tribonacci_iter n = failwith "Unanswered" +let rec tribonacci_tail n a b c = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F3/3-tribonacci/test.ml b/exercises/smelodesousa/F3/3-tribonacci/test.ml new file mode 100644 index 0000000..3ae4567 --- /dev/null +++ b/exercises/smelodesousa/F3/3-tribonacci/test.ml @@ -0,0 +1,61 @@ +open Test_lib +open Report + +let check_recursion name cb = + let module Error = struct + exception RecursionCall + end in + find_binding code_ast name @@ fun expr -> + let contains_recursion_call = + Parsetree.( + function + | { pexp_desc = Pexp_apply ({ pexp_desc = Pexp_ident { txt = id } }, _) } + -> + if Longident.last id = name then raise Error.RecursionCall else [] + | _ -> []) + in + try + ast_check_expr ~on_expression:contains_recursion_call expr; + [ + Message + ( [ + Text "The function"; + Code name; + Text "does not contain a recursive call"; + ], + Failure ); + ] + with Error.RecursionCall -> cb () + +let test_a_with_solution = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "tribonacci" ], + check_recursion "tribonacci" @@ fun () -> + test_function_1_against_solution [%ty: int -> int] "tribonacci" + ~sampler:(fun () -> Random.int 25) + ~gen:39 [ 0; -10 ] ) + +let test_b_with_solution = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "tribonacci_iter" ], + test_function_1_against_solution [%ty: int -> int] "tribonacci_iter" + ~sampler:(fun () -> Random.int 50) + ~gen:39 [ 0; -10 ] ) + +let test_c_with_solution = + set_progress "Grading exercise 3"; + Section + ( [ Text "Exercise 3: "; Code "tribonacci_tail" ], + check_recursion "tribonacci_tail" @@ fun () -> + test_function_4_against_solution [%ty: int -> int -> int -> int -> int] + "tribonacci_tail" + ~sampler:(fun () -> (Random.int 5000, 1, 1, 1)) + ~gen:39 + [ (0, 1, 1, 1); (5000, 1, 1, 1); (-10, 1, 1, 1); (6, 1, 1, 1) ] ) + +let () = + set_result @@ ast_sanity_check code_ast + @@ fun () -> + [ test_a_with_solution; test_b_with_solution; test_c_with_solution ] diff --git a/exercises/smelodesousa/F4/4-fun-1/descr.md b/exercises/smelodesousa/F4/4-fun-1/descr.md new file mode 100644 index 0000000..f0215e9 --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-1/descr.md @@ -0,0 +1,16 @@ +Provide the type of the following expressions: + +1. `let x = read_int () in let y = read_float () in (x+1,y)`
+2. `let f a b c = b (c + a)`
+3. `let f a b c = b c`
+4. `let f a (b: int -> int) c = a (b c)`
+5. `let f a b c = a (b c)`
+6. `let f a b c = c (b a)`
+7. `let o = ref (List.tl [1])`
+8. `let x = ref []`
+9. `let x = [ ( + ) 3 ; succ ; (fun x y -> List.length x+ y) [1;5] ]`
+10. `let x y = y 1 in x (fun z -> z + 1) `
+11. `let f x y = function z -> y (List.map x z)`

+ +**Note:** If necessary, use the type `'_weak1`. It should be used as follows:
+ `type answer = _weak1`, which means, without the apostrophe. \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-fun-1/meta.json b/exercises/smelodesousa/F4/4-fun-1/meta.json new file mode 100644 index 0000000..d8ae541 --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-1/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Functions I", + "identifier" : "4.7", + "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F4/4-type-1"] +} diff --git a/exercises/smelodesousa/F4/4-fun-1/prelude.ml b/exercises/smelodesousa/F4/4-fun-1/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F4/4-fun-1/prepare.ml b/exercises/smelodesousa/F4/4-fun-1/prepare.ml new file mode 100644 index 0000000..0e0d599 --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-1/prepare.ml @@ -0,0 +1,3 @@ +type _weak1 +type _weak2 +type _weak3 diff --git a/exercises/smelodesousa/F4/4-fun-1/solution.ml b/exercises/smelodesousa/F4/4-fun-1/solution.ml new file mode 100644 index 0000000..23e10dc --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-1/solution.ml @@ -0,0 +1,21 @@ +type ('a, 'b, 'c) q1 = int * float + +type ('a, 'b, 'c) q2 = int -> (int -> 'a) -> int -> 'a + +type ('a, 'b, 'c) q3 = 'a -> ('b -> 'c) -> 'b -> 'c + +type ('a, 'b, 'c) q4 = (int -> 'a) -> (int -> int) -> int -> 'a + +type ('a, 'b, 'c) q5 = ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b + +type ('a, 'b, 'c) q6 = 'a -> ('a -> 'b) -> ('b -> 'c) -> 'c + +type ('a, 'b, 'c) q7 = int list ref + +type ('a, 'b, 'c) q8 = _weak1 list ref + +type ('a, 'b, 'c) q9 = (int -> int) list + +type ('a, 'b, 'c) q10 = int + +type ('a, 'b, 'c) q11 = ('a -> 'b) -> ('b list -> 'c) -> 'a list -> 'c \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-fun-1/template.ml b/exercises/smelodesousa/F4/4-fun-1/template.ml new file mode 100644 index 0000000..55f6b3f --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-1/template.ml @@ -0,0 +1,21 @@ +type ('a, 'b, 'c) q1 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q2 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q3 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q4 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q5 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q6 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q7 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q8 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q9 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q10 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q11 = To_Answer_Replace_with_your_solution \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-fun-1/test.ml b/exercises/smelodesousa/F4/4-fun-1/test.ml new file mode 100644 index 0000000..fc990fc --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-1/test.ml @@ -0,0 +1,208 @@ +open Test_lib +open Report + + +let correct_answer id name = + Section ([ Text id ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Correct answer" ], Success 5)]) + +let wrong_answer id name = + Section ([ Text id ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Wrong answer" ], Failure)]) + +let compatible_type ~expected:exp got = + match Introspection.compatible_type exp ("Code." ^ got) with + | Introspection.Absent -> false + | Introspection.Incompatible _ -> false + | Introspection.Present () -> true + + +type ('a, 'b, 'c) correct_q1 = int * float +type ('a, 'b, 'c) incorrect1_q1 = bool -> float +type ('a, 'b, 'c) incorrect2_q1 = int -> bool + +let ex1 = + let a1 = compatible_type "correct_q1" "q1" in + let a2 = compatible_type "incorrect1_q1" "q1" in + let a3 = compatible_type "incorrect2_q1" "q1" in + + match a1,a2,a3 with + | true, false, false -> correct_answer "Exercise 1: " "q1" + | _ -> wrong_answer "Exercise 1: " "q1" + +(* 2 *) +type ('a, 'b, 'c) correct1_q2 = int -> (int -> 'a) -> int -> 'a +type ('a, 'b, 'c) correct2_q2 = int -> (int -> int) -> int -> int +type ('a, 'b, 'c) correct3_q2 = int -> (int -> float) -> int -> float +type ('a, 'b, 'c) incorrect1_q2 = float -> (int -> 'a) -> int -> 'a +type ('a, 'b, 'c) incorrect2_q2 = int -> (float -> 'a) -> int -> 'a +type ('a, 'b, 'c) incorrect3_q2 = int -> (int -> 'a) -> float -> 'a + +let ex2 = + let a1 = compatible_type "correct1_q2" "q2" in + let a2 = compatible_type "correct2_q2" "q2" in + let a3 = compatible_type "correct3_q2" "q2" in + let a4 = compatible_type "incorrect1_q2" "q2" in + let a5 = compatible_type "incorrect2_q2" "q2" in + let a6 = compatible_type "incorrect3_q2" "q2" in + + match a1,a2,a3,a4,a5,a6 with + | true, true, true, false, false, false -> correct_answer "Exercise 2: " "q2" + | _ -> wrong_answer "Exercise 2: " "q2" + +(* 3 *) +type ('a, 'b, 'c) correct1_q3 = 'a -> ('b -> 'c) -> 'b -> 'c +type ('a, 'b, 'c) correct2_q3 = int -> (float -> bool) -> float -> bool +type ('a, 'b, 'c) correct3_q3 = float -> (string -> int) -> string -> int + +let ex3 = + let a1 = compatible_type "correct1_q3" "q3" in + let a2 = compatible_type "correct2_q3" "q3" in + let a3 = compatible_type "correct3_q3" "q3" in + + match a1,a2,a3 with + | true, true, true -> correct_answer "Exercise 3: " "q3" + | _ -> wrong_answer "Exercise 3: " "q3" + +(* 4 *) +type ('a, 'b, 'c) correct1_q4 = (int -> 'a) -> (int -> int) -> int -> 'a +type ('a, 'b, 'c) correct2_q4 = (int -> string) -> (int -> int) -> int -> string +type ('a, 'b, 'c) correct3_q4 = (int -> bool) -> (int -> int) -> int -> bool +type ('a, 'b, 'c) incorrect1_q4 = (float -> 'a) -> (int -> int) -> int -> 'a +type ('a, 'b, 'c) incorrect2_q4 = (int -> 'a) -> (float -> int) -> int -> 'a +type ('a, 'b, 'c) incorrect3_q4 = (int -> 'a) -> (int -> float) -> int -> 'a +type ('a, 'b, 'c) incorrect4_q4 = (int -> 'a) -> (int -> int) -> float -> 'a + + +let ex4 = + let long_name, short_name = "Exercise 4: ", "q4" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("correct2_"^short_name) short_name in + let a3 = compatible_type ("correct3_"^short_name) short_name in + let a4 = compatible_type ("incorrect1_"^short_name) short_name in + let a5 = compatible_type ("incorrect2_"^short_name) short_name in + let a6 = compatible_type ("incorrect3_"^short_name) short_name in + let a7 = compatible_type ("incorrect4_"^short_name) short_name in + + match a1,a2,a3,a4,a5,a6,a7 with + | true,true,true,false,false,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +(* 5 *) +type ('a, 'b, 'c) correct1_q5 = ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b +type ('a, 'b, 'c) correct2_q5 = (int -> float) -> (bool -> int) -> bool -> float +type ('a, 'b, 'c) correct3_q5 = (string -> bool) -> (char -> string) -> char -> bool + +let ex5 = + let long_name, short_name = "Exercise 5: ", "q5" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("correct2_"^short_name) short_name in + let a3 = compatible_type ("correct3_"^short_name) short_name in + + match a1,a2,a3 with + | true,true,true -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +(* 6 *) +type ('a, 'b, 'c) correct1_q6 = 'a -> ('a -> 'b) -> ('b -> 'c) -> 'c +type ('a, 'b, 'c) correct2_q6 = int -> (int -> bool) -> (bool -> char) -> char +type ('a, 'b, 'c) correct3_q6 = float -> (float -> string) -> (string -> int) -> int + +let ex6 = + let long_name, short_name = "Exercise 6: ", "q6" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("correct2_"^short_name) short_name in + let a3 = compatible_type ("correct3_"^short_name) short_name in + + match a1,a2,a3 with + | true,true,true -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + + +(* 7 *) +type ('a, 'b, 'c) correct1_q7 = int list ref +type ('a, 'b, 'c) incorrect1_q7 = float list ref +type ('a, 'b, 'c) incorrect2_q7 = string ref + +let ex7 = + let long_name, short_name = "Exercise 7: ", "q7" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("incorrect1_"^short_name) short_name in + let a3 = compatible_type ("incorrect2_"^short_name) short_name in + + match a1,a2,a3 with + | true,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +(* 8 *) +type ('a, 'b, 'c) correct1_q8 = _weak1 list ref +type ('a, 'b, 'c) incorrect1_q8 = float list ref +type ('a, 'b, 'c) incorrect2_q8 = string ref + +let ex8 = + let long_name, short_name = "Exercise 8: ", "q8" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("incorrect1_"^short_name) short_name in + let a3 = compatible_type ("incorrect2_"^short_name) short_name in + + match a1,a2,a3 with + | true,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +(* 9 *) +type ('a, 'b, 'c) correct1_q9 = (int -> int) list +type ('a, 'b, 'c) incorrect1_q9 = (float -> int) list +type ('a, 'b, 'c) incorrect2_q9 = (int -> float) list +type ('a, 'b, 'c) incorrect3_q9 = float + +let ex9 = + let long_name, short_name = "Exercise 9: ", "q9" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("incorrect1_"^short_name) short_name in + let a3 = compatible_type ("incorrect2_"^short_name) short_name in + let a4 = compatible_type ("incorrect3_"^short_name) short_name in + + match a1,a2,a3,a4 with + | true,false,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +(* 10 *) +type ('a, 'b, 'c) correct1_q10 = int +type ('a, 'b, 'c) incorrect1_q10 = float + +let ex10 = + let long_name, short_name = "Exercise 10: ", "q10" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("incorrect1_"^short_name) short_name in + + match a1,a2 with + | true,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +(* 11 *) +type ('a, 'b, 'c) correct1_q11 = ('a -> 'b) -> ('b list -> 'c) -> 'a list -> 'c +type ('a, 'b, 'c) correct2_q11 = (int -> float) -> (float list -> string) -> int list -> string +type ('a, 'b, 'c) correct3_q11 = (bool -> char) -> (char list -> int) -> bool list -> int +type ('a, 'b, 'c) incorrect1_q11 = ('a -> 'b) -> (int -> 'c) -> 'a list -> 'c +type ('a, 'b, 'c) incorrect1_q11 = ('a -> 'b) -> ('b list -> 'c) -> float -> 'c + + +let ex11 = + let long_name, short_name = "Exercise 11: ", "q11" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("correct2_"^short_name) short_name in + let a3 = compatible_type ("correct3_"^short_name) short_name in + let a4 = compatible_type ("incorrect1_"^short_name) short_name in + let a5 = compatible_type ("incorrect2_"^short_name) short_name in + + match a1,a2,a3,a4,a5 with + | true,true,true,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ex1; ex2; ex3; ex4; ex5; ex6; ex7; ex8; ex9; ex10; ex11] diff --git a/exercises/smelodesousa/F4/4-fun-2/descr.md b/exercises/smelodesousa/F4/4-fun-2/descr.md new file mode 100644 index 0000000..1b1d055 --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-2/descr.md @@ -0,0 +1,47 @@ +Provide the type of the following expressions: + +1. `let rec ones = 1::ones` + +2. Assuming the previous declaration, what returns `List.length ones`? + + A) The list is endless, so it runs until it reaches _stack overflow_.
+ B) The list is endless, so it runs endlessly.
+ C) Assuming the list is infinite, it returns an arbitrary value.
+ D) Assuming the list is infinite, the execution uses up all memory until it causes a `BSOD`.

+ +3. Assuming the previous declaration, what returns `List.hd ones`? + + A) The function returns `Failure "hd"`.
+ B) The list is infinite, so it runs endlessly.
+ C) Returns an endless list of 1s except the first.
+ D) 1.

+ +4. `fun x -> fun y -> fun z -> x z (y z)` + +5. `let x = (List.tl [3]) in 1::x` + +6. Consider the following declarations: `let l = ref []` and `let nl = 1::!l`. What does the following declaration do: `let nll = 'c':: !l`? + + A) `nll` becomes equal to `['c'; 1]`.
+ B) `nll` becomes equal to `[1; 'c']`.
+ C) `nll` becomes equal to `[99; 1]`, where `99` is the ASCII code of `c`.
+ D) Returns a type error.

+ +7. `let k = fun x y -> x in let i = fun x -> x in k k i` + +8. Consider the following code: + + ```ocaml + let b = true + let f0 = fun x -> x+1 + let f = fun x -> if b then f0 else fun y -> x y + let f = fun x -> if b then f else fun y -> x y + let f = fun x -> if b then f else fun y -> x y + let f = fun x -> if b then f else fun y -> x y + ``` + + What is the type of the last `f`? + + +**Note:** If necessary, use the type `'_weak1`. It should be used as follows:
+ `type answer = _weak1`, which means, without the apostrophe. \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-fun-2/meta.json b/exercises/smelodesousa/F4/4-fun-2/meta.json new file mode 100644 index 0000000..81538fa --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-2/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Functions II", + "identifier" : "4.7", + "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], + "backward_exercises": ["smelodesousa/F4/4-fun-1"] +} diff --git a/exercises/smelodesousa/F4/4-fun-2/prelude.ml b/exercises/smelodesousa/F4/4-fun-2/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F4/4-fun-2/prepare.ml b/exercises/smelodesousa/F4/4-fun-2/prepare.ml new file mode 100644 index 0000000..52c758b --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-2/prepare.ml @@ -0,0 +1,6 @@ +type choice = + | A | B | C | D | To_Answer of string + +type _weak1 +type _weak2 +type _weak3 diff --git a/exercises/smelodesousa/F4/4-fun-2/solution.ml b/exercises/smelodesousa/F4/4-fun-2/solution.ml new file mode 100644 index 0000000..274f72d --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-2/solution.ml @@ -0,0 +1,15 @@ +type ('a, 'b, 'c) q1 = int list + +let q2 = A + +let q3 = D + +type ('a, 'b, 'c) q4 = ('a -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'c + +type ('a, 'b, 'c) q5 = int list + +let q6 = D + +type ('a, 'b, 'c) q7 = _weak1 -> _weak2 -> _weak1 + +type ('a, 'b, 'c) q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-fun-2/template.ml b/exercises/smelodesousa/F4/4-fun-2/template.ml new file mode 100644 index 0000000..3fa7778 --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-2/template.ml @@ -0,0 +1,15 @@ +type ('a, 'b, 'c) q1 = To_Answer_Replace_with_your_solution + +let q2 = To_Answer "Replace with your answer" + +let q3 = To_Answer "Replace with your answer" + +type ('a, 'b, 'c) q4 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q5 = To_Answer_Replace_with_your_solution + +let q6 = To_Answer "Replace with your answer" + +type ('a, 'b, 'c) q7 = To_Answer_Replace_with_your_solution + +type ('a, 'b, 'c) q8 = To_Answer_Replace_with_your_solution \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-fun-2/test.ml b/exercises/smelodesousa/F4/4-fun-2/test.ml new file mode 100644 index 0000000..d55a261 --- /dev/null +++ b/exercises/smelodesousa/F4/4-fun-2/test.ml @@ -0,0 +1,186 @@ +open Test_lib +open Report + +let correct_answer id name = + Section ([ Text id ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Correct answer" ], Success 5)]) + +let wrong_answer id name = + Section ([ Text id ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Wrong answer" ], Failure)]) + +let compatible_type ~expected:exp got = + match Introspection.compatible_type exp ("Code." ^ got) with + | Introspection.Absent -> false + | Introspection.Incompatible _ -> false + | Introspection.Present () -> true + +(* 1 *) +type ('a, 'b, 'c) correct1_q1 = int list +type ('a, 'b, 'c) incorrect1_q1 = float list +type ('a, 'b, 'c) incorrect2_q1 = bool + +let ex1 = + let long_name, short_name = "Exercise 1: ", "q1" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("incorrect1_"^short_name) short_name in + let a3 = compatible_type ("incorrect2_"^short_name) short_name in + + + match a1,a2,a3 with + | true,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +let ex2 = + set_progress "Correcting question 2" ; + Section ([ Text "Exercise 2: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "q2") + +let ex3 = + set_progress "Correcting question 3" ; + Section ([ Text "Exercise 3: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "q3") + + +(* 4 *) +type ('a, 'b, 'c) correct1_q4 = ('a -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'c +type ('a, 'b, 'c) correct2_q4 = (int -> bool -> char) -> (int -> bool) -> int -> char +type ('a, 'b, 'c) correct3_q4 = (float -> int -> string) -> (float -> int) -> float -> string + +let ex4 = + let long_name, short_name = "Exercise 4: ", "q4" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("correct2_"^short_name) short_name in + let a3 = compatible_type ("correct3_"^short_name) short_name in + + match a1,a2,a3 with + | true,true,true -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + + +(* 5 *) +type ('a, 'b, 'c) correct1_q5 = int list +type ('a, 'b, 'c) incorrect1_q5 = float list +type ('a, 'b, 'c) incorrect2_q5 = char + +let ex5 = + let long_name, short_name = "Exercise 5: ", "q5" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("incorrect1_"^short_name) short_name in + let a3 = compatible_type ("incorrect2_"^short_name) short_name in + + match a1,a2,a3 with + | true,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +let ex6 = + set_progress "Correcting question 6" ; + Section ([ Text "Exercise 6: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "q6") + + +(* 7 *) +type ('a, 'b, 'c) correct1_q7 = _weak1 -> _weak2 -> _weak1 +type ('a, 'b, 'c) incorrect1_q7 = int -> _weak2 -> _weak1 +type ('a, 'b, 'c) incorrect2_q7 = _weak1 -> int -> _weak1 +type ('a, 'b, 'c) incorrect3_q7 = _weak1 -> _weak2 -> int + +let ex7 = + let long_name, short_name = "Exercise 7: ", "q7" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("incorrect1_"^short_name) short_name in + let a3 = compatible_type ("incorrect2_"^short_name) short_name in + let a4 = compatible_type ("incorrect3_"^short_name) short_name in + + match a1,a2,a3,a4 with + | true,false,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + + +(* 8 *) +type ('a, 'b, 'c) correct1_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect1_q8 = ((((float -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect2_q8 = ((((int -> float) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect3_q8 = ((((int -> int) -> float -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect4_q8 = ((((int -> int) -> int -> float) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect5_q8 = ((((int -> int) -> int -> int) -> (float -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect6_q8 = ((((int -> int) -> int -> int) -> (int -> float) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect7_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> float -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect8_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> float) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect9_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((float -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect10_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> float) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect11_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> float -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect12_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> float) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect13_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (float -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect14_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> float) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect15_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> float -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect16_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> float) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect17_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((float -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect18_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> float) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect19_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> float -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect20_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> float) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect21_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (float -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect22_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> float) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect23_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> float -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect24_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> float) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect25_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((float -> int) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect26_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> float) -> int -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect27_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> float -> int) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect28_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> float) -> (int -> int) -> int -> int +type ('a, 'b, 'c) incorrect29_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (float -> int) -> int -> int +type ('a, 'b, 'c) incorrect30_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> float) -> int -> int +type ('a, 'b, 'c) incorrect31_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> float -> int +type ('a, 'b, 'c) incorrect32_q8 = ((((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> (((int -> int) -> int -> int) -> (int -> int) -> int -> int) -> ((int -> int) -> int -> int) -> (int -> int) -> int -> float + +let ex8 = + let long_name, short_name = "Exercise 8: ", "q8" in + let a1 = compatible_type ("correct1_"^short_name) short_name in + let a2 = compatible_type ("incorrect1_"^short_name) short_name in + let a3 = compatible_type ("incorrect2_"^short_name) short_name in + let a4 = compatible_type ("incorrect3_"^short_name) short_name in + let a5 = compatible_type ("incorrect4_"^short_name) short_name in + let a6 = compatible_type ("incorrect5_"^short_name) short_name in + let a7 = compatible_type ("incorrect6_"^short_name) short_name in + let a8 = compatible_type ("incorrect7_"^short_name) short_name in + let a9 = compatible_type ("incorrect8_"^short_name) short_name in + let a10 = compatible_type ("incorrect9_"^short_name) short_name in + let a11 = compatible_type ("incorrect10_"^short_name) short_name in + let a12 = compatible_type ("incorrect11_"^short_name) short_name in + let a13 = compatible_type ("incorrect12_"^short_name) short_name in + let a14 = compatible_type ("incorrect13_"^short_name) short_name in + let a15 = compatible_type ("incorrect14_"^short_name) short_name in + let a16 = compatible_type ("incorrect15_"^short_name) short_name in + let a17 = compatible_type ("incorrect16_"^short_name) short_name in + let a18 = compatible_type ("incorrect17_"^short_name) short_name in + let a19 = compatible_type ("incorrect18_"^short_name) short_name in + let a20 = compatible_type ("incorrect19_"^short_name) short_name in + let a21 = compatible_type ("incorrect20_"^short_name) short_name in + let a22 = compatible_type ("incorrect21_"^short_name) short_name in + let a23 = compatible_type ("incorrect22_"^short_name) short_name in + let a24 = compatible_type ("incorrect23_"^short_name) short_name in + let a25 = compatible_type ("incorrect24_"^short_name) short_name in + let a26 = compatible_type ("incorrect25_"^short_name) short_name in + let a27 = compatible_type ("incorrect26_"^short_name) short_name in + let a28 = compatible_type ("incorrect27_"^short_name) short_name in + let a29 = compatible_type ("incorrect28_"^short_name) short_name in + let a30 = compatible_type ("incorrect29_"^short_name) short_name in + let a31 = compatible_type ("incorrect30_"^short_name) short_name in + let a32 = compatible_type ("incorrect31_"^short_name) short_name in + let a33 = compatible_type ("incorrect32_"^short_name) short_name in + + match a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33 with + | true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ ex1; ex2; ex3; ex4; ex5; ex6; ex7; ex8 ] diff --git a/exercises/smelodesousa/F4/4-type-1/descr.md b/exercises/smelodesousa/F4/4-type-1/descr.md new file mode 100644 index 0000000..afbe851 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-1/descr.md @@ -0,0 +1,5 @@ +For each expression, specify their type: + +1. `let f1 x = !x` +2. `let f2 g x y = if g x then y x else g` +3. `let f3 h = let x = ref true in if h x then x:= false; !x` \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-1/meta.json b/exercises/smelodesousa/F4/4-type-1/meta.json new file mode 100644 index 0000000..990f661 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-1/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Verify the type I", + "identifier" : "4.1", + "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F3/3-stars-in-the-form-of-textual-fractals"] +} diff --git a/exercises/smelodesousa/F4/4-type-1/prelude.ml b/exercises/smelodesousa/F4/4-type-1/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F4/4-type-1/prepare.ml b/exercises/smelodesousa/F4/4-type-1/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F4/4-type-1/solution.ml b/exercises/smelodesousa/F4/4-type-1/solution.ml new file mode 100644 index 0000000..616a666 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-1/solution.ml @@ -0,0 +1,5 @@ +type 'a p1 = 'a ref -> 'a + +type 'a p2 = ('a -> bool) -> 'a -> ('a -> 'a -> bool) -> 'a -> bool + +type 'a p3 = (bool ref -> bool) -> bool \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-1/template.ml b/exercises/smelodesousa/F4/4-type-1/template.ml new file mode 100644 index 0000000..3a41f56 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-1/template.ml @@ -0,0 +1,5 @@ +type 'a p1 = Replace_with_your_solution + +type 'a p2 = Replace_with_your_solution + +type 'a p3 = Replace_with_your_solution \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-1/test.ml b/exercises/smelodesousa/F4/4-type-1/test.ml new file mode 100644 index 0000000..4eb18e2 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-1/test.ml @@ -0,0 +1,80 @@ +open Test_lib +open Report + +let correct_answer id name = + Section ([ Text id ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Correct answer" ], Success 5)]) + +let wrong_answer id name = + Section ([ Text id ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Wrong answer" ], Failure)]) + +let compatible_type ~expected:exp got = + match Introspection.compatible_type exp ("Code." ^ got) with + | Introspection.Absent -> false + | Introspection.Incompatible _ -> false + | Introspection.Present () -> true + +(* 1 *) +type 'a correct1_p1 = 'a ref -> 'a +type 'a correct2_p1 = int ref -> int +type 'a correct3_p1 = float ref -> float +type 'a wrong1_p1 = int -> 'a + +let ex1 = + let long_name, short_name = "Exercise 1 ", "p1" in + let r1 = compatible_type ("correct1_"^short_name) short_name in + let r2 = compatible_type ("correct2_"^short_name) short_name in + let r3 = compatible_type ("correct3_"^short_name) short_name in + let r4 = compatible_type ("wrong1_"^short_name) short_name in + + match r1,r2,r3,r4 with + | true,true,true,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +(* 2 *) +type 'a correct1_p2 = ('a -> bool) -> 'a -> ('a -> 'a -> bool) -> 'a -> bool +type 'a correct2_p2 = (int -> bool) -> int -> (int -> int -> bool) -> int -> bool +type 'a correct3_p2 = (float -> bool) -> float -> (float -> float -> bool) -> float -> bool +type 'a wrong1_p2 = ('a -> int) -> 'a -> ('a -> 'a -> bool) -> 'a -> bool +type 'a wrong2_p2 = ('a -> bool) -> 'a -> ('a -> 'a -> int) -> 'a -> bool +type 'a wrong3_p2 = ('a -> bool) -> 'a -> ('a -> 'a -> bool) -> 'a -> int + +let ex2 = + let long_name, short_name = "Exercise 2 ", "p2" in + let r1 = compatible_type ("correct1_"^short_name) short_name in + let r2 = compatible_type ("correct2_"^short_name) short_name in + let r3 = compatible_type ("correct3_"^short_name) short_name in + let r4 = compatible_type ("wrong1_"^short_name) short_name in + let r5 = compatible_type ("wrong2_"^short_name) short_name in + let r6 = compatible_type ("wrong3_"^short_name) short_name in + + match r1,r2,r3,r4,r5,r6 with + | true,true,true,false,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +(* 3 *) +type 'a correct1_p3 = (bool ref -> bool) -> bool +type 'a wrong1_p3 = (int ref -> bool) -> bool +type 'a wrong2_p3 = (bool ref -> int) -> bool +type 'a wrong3_p3 = (bool ref -> bool) -> int +type 'a wrong4_p3 = (float -> bool) -> bool + +let ex3 = + let long_name, short_name = "Exercise 3 ", "p3" in + let r1 = compatible_type ("correct1_"^short_name) short_name in + let r2 = compatible_type ("wrong1_"^short_name) short_name in + let r3 = compatible_type ("wrong2_"^short_name) short_name in + let r4 = compatible_type ("wrong3_"^short_name) short_name in + let r5 = compatible_type ("wrong4_"^short_name) short_name in + + match r1,r2,r3,r4,r5 with + | true,false,false,false,false -> correct_answer long_name short_name + | _ -> wrong_answer long_name short_name + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ ex1; ex2; ex3] diff --git a/exercises/smelodesousa/F4/4-type-2/descr.md b/exercises/smelodesousa/F4/4-type-2/descr.md new file mode 100644 index 0000000..5c499d7 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-2/descr.md @@ -0,0 +1,23 @@ +Consider the following code: + +```ocaml +let rec f x = + match x with + | 0 -> 1 + | 1 -> 1 + | n -> f(n-1) + f(n-2) +``` + +1. Does the function always terminate? + + A) True
+ B) False

+ +2. What is the type of the function? + + A) `int -> int`
+ B) `int -> int -> int`
+ C) `int`
+ D) `ERROR`
+ +**Note:** If you believe that the correct option is `A` then you should answer as follows: `let answer = A`. diff --git a/exercises/smelodesousa/F4/4-type-2/meta.json b/exercises/smelodesousa/F4/4-type-2/meta.json new file mode 100644 index 0000000..59148f3 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-2/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Verify the type II", + "identifier" : "4.2", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F1/1-fun-calls"] +} diff --git a/exercises/smelodesousa/F4/4-type-2/prelude.ml b/exercises/smelodesousa/F4/4-type-2/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F4/4-type-2/prepare.ml b/exercises/smelodesousa/F4/4-type-2/prepare.ml new file mode 100644 index 0000000..3dd5880 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-2/prepare.ml @@ -0,0 +1,2 @@ +type choice = + | A | B | C | D | Unanswered of string \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-2/solution.ml b/exercises/smelodesousa/F4/4-type-2/solution.ml new file mode 100644 index 0000000..5719556 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-2/solution.ml @@ -0,0 +1,3 @@ +let p1 = B + +let p2 = A \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-2/template.ml b/exercises/smelodesousa/F4/4-type-2/template.ml new file mode 100644 index 0000000..d2cb9d1 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-2/template.ml @@ -0,0 +1,3 @@ +let p1 = failwith "Replace with your solution" + +let p2 = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-2/test.ml b/exercises/smelodesousa/F4/4-type-2/test.ml new file mode 100644 index 0000000..f802766 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-2/test.ml @@ -0,0 +1,21 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise 1" ; + Section ([ Text "Exercise 1: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p1") + +let ex2 = + set_progress "Grading exercise 2" ; + Section ([ Text "Exercise 2: " ; Code "solution" ], + test_variable_against_solution + [%ty: choice ] + "p2") + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ ex1; ex2] diff --git a/exercises/smelodesousa/F4/4-type-3/descr.md b/exercises/smelodesousa/F4/4-type-3/descr.md new file mode 100644 index 0000000..cf47264 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-3/descr.md @@ -0,0 +1,21 @@ +Consider the following piece of code: + +```ocaml +let rec f x y = + match x with + | 0 -> y + | 2 -> Printf.printf "Case 2\n"; f y 2 + | n -> Printf.printf "Case n\n"; f y 0 +``` + +1. Does the function always terminate? + + A) True
+ B) False

+ +2. The function is properly typified with which of the types? + + A) `int -> int -> int`
+ B) `int -> int -> unit`
+ C) `unit -> unit -> unit`
+ D) `ERROR`

diff --git a/exercises/smelodesousa/F4/4-type-3/meta.json b/exercises/smelodesousa/F4/4-type-3/meta.json new file mode 100644 index 0000000..663b929 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-3/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Check type III", + "identifier": "4.3", + "authors": [ + ["Rui Barata", "rui.barata@ubi.pt"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F4/4-type-2"] +} diff --git a/exercises/smelodesousa/F4/4-type-3/prelude.ml b/exercises/smelodesousa/F4/4-type-3/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F4/4-type-3/prepare.ml b/exercises/smelodesousa/F4/4-type-3/prepare.ml new file mode 100644 index 0000000..cea7267 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-3/prepare.ml @@ -0,0 +1 @@ +type choice = A | B | C | D | Unanswered diff --git a/exercises/smelodesousa/F4/4-type-3/solution.ml b/exercises/smelodesousa/F4/4-type-3/solution.ml new file mode 100644 index 0000000..1364091 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-3/solution.ml @@ -0,0 +1,4 @@ +let q1 = B +(* For example: f 2 2 does not finish *) + +let q2 = A \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-3/template.ml b/exercises/smelodesousa/F4/4-type-3/template.ml new file mode 100644 index 0000000..9bac642 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-3/template.ml @@ -0,0 +1,2 @@ +let q1 = Unanswered +let q2 = Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-3/test.ml b/exercises/smelodesousa/F4/4-type-3/test.ml new file mode 100644 index 0000000..3ccab2b --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-3/test.ml @@ -0,0 +1,16 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q1" ) + +let ex2 = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q2" ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ ex1; ex2 ] diff --git a/exercises/smelodesousa/F4/4-type-4/descr.md b/exercises/smelodesousa/F4/4-type-4/descr.md new file mode 100644 index 0000000..992b1d7 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-4/descr.md @@ -0,0 +1,22 @@ +Consider the following piece of code: + +```ocaml +let rec f x = + match x with + | 0 -> 0 + | 1 -> f 0 + | 2 -> f (x+1) + | 3 -> (f 1)+2 + | n -> n+1 +``` + +1. Does the function always terminate? + + A) True
+ B) False

+ +2. The function is properly typified with which of the types? + + A) `int`
+ B) `int -> int`
+ C) `ERROR`
\ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-4/meta.json b/exercises/smelodesousa/F4/4-type-4/meta.json new file mode 100644 index 0000000..91b8c08 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-4/meta.json @@ -0,0 +1,12 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Check type IV", + "identifier": "4.4", + "authors": [ + ["Rui Barata", "rui.barata@ubi.pt"], + ["Simão Sousa", "desousa@di.ubi.pt"] + ], + "backward_exercises": ["smelodesousa/F4/4-type-3"] +} diff --git a/exercises/smelodesousa/F4/4-type-4/prelude.ml b/exercises/smelodesousa/F4/4-type-4/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F4/4-type-4/prepare.ml b/exercises/smelodesousa/F4/4-type-4/prepare.ml new file mode 100644 index 0000000..cea7267 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-4/prepare.ml @@ -0,0 +1 @@ +type choice = A | B | C | D | Unanswered diff --git a/exercises/smelodesousa/F4/4-type-4/solution.ml b/exercises/smelodesousa/F4/4-type-4/solution.ml new file mode 100644 index 0000000..4752809 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-4/solution.ml @@ -0,0 +1,2 @@ +let q1 = A +let q2 = B \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-4/template.ml b/exercises/smelodesousa/F4/4-type-4/template.ml new file mode 100644 index 0000000..9bac642 --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-4/template.ml @@ -0,0 +1,2 @@ +let q1 = Unanswered +let q2 = Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F4/4-type-4/test.ml b/exercises/smelodesousa/F4/4-type-4/test.ml new file mode 100644 index 0000000..3ccab2b --- /dev/null +++ b/exercises/smelodesousa/F4/4-type-4/test.ml @@ -0,0 +1,16 @@ +open Test_lib +open Report + +let ex1 = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q1" ) + +let ex2 = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "solution" ], + test_variable_against_solution [%ty: choice] "q2" ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ ex1; ex2 ] diff --git a/exercises/smelodesousa/F5/5-absolute-majority/descr.md b/exercises/smelodesousa/F5/5-absolute-majority/descr.md new file mode 100644 index 0000000..6c215b6 --- /dev/null +++ b/exercises/smelodesousa/F5/5-absolute-majority/descr.md @@ -0,0 +1,70 @@ + + + + + +# Introduction + +Imagine an electoral process in which candidates have an integer that identifies them (candidate 1, 2, 3, etc.). + +In this configuration, a vote is the integer representing the candidate chosen by the voter. For example, a vote with $3$ is a vote towards candidate number $3$. + +The votes are placed on an array. We pretend to know if there is an absolute majority in the election. In this case the candidate with the majority of the votes is elected. + +# Objectives + +1. Define the `majority : int array -> int` function that returns the integer belonging to the candidate that has the majority of the votes. If there is no majority, the exception `Not_found` is thrown. + +There is an algorithm, called *MJRTY algorithm*, created by R. Boyer and J. Moore in 1980, which determines if there is a majority winner in $2\times N$ comparisons and only needs $3$ variables apart from the votes array. Another advantage is that it doesn't need to know how many candidates the election has. + +Reference to the algorithm: + +> Robert S. Boyer, J. Strother Moore. *MJRTY - A Fast Majority Vote Algorithm*. In R.S. Boyer (ed.), Automated Reasoning: Essays in Honor of Woody Bledsoe, Automated Reasoning Series, Kluwer Academic Publishers, Dordrecht, The Netherlands, 1991, pp. 105-117. http://www.cs.utexas.edu/users/boyer/mjrty.ps.Z + +Pseudocode for the algorithm: + +```pseudocode +Prelude: The candidates are identified by integers. + a is the array containing the votes + +let m be an integer and i a counter with a starting value of 0 + +for each element x of a + if i = 0 then m := x and i := 1 + else if m = x then increment i + else decrement i + +if i = 0 then throw Not_found +if i > ⌊|a|/2⌋ then return m + +reset i to 0 +for each element x of a + if x = m then increment i + +if i > ⌊|a|/2⌋ then return m +else throw Not_found +``` + +The first cicle of this algorithm can be graphically represented in the following way: + + +![](https://i.imgur.com/TifapTj.png) + +(taken from wikipedia) + + +We know the winner is decided if his advantage (value of $i$) is bigger than half of the votes. We also know that the voting hasn't missed any winners if that same value is $0$. In every other case, we can't conclude anything, meaning we need a new count, this time of the votes for the proposed winner. + +In the image example, if the voting only had the first 7 votes, then the yellow candidate would have been excluded from being a winner in the second cycle. + +2. Implement the `mjrty :int list -> int` function that implements this algorithm.
+ For example `mjrty [1; 1; 2; 1; 2; 3; 3; 2; 2; 2; 1; 2; 2; 3; 2; 2] = 2`
+ If the votes are inconclusive (no absolute majority), the function should throw a `Not_found` exception.
\ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-absolute-majority/meta.json b/exercises/smelodesousa/F5/5-absolute-majority/meta.json new file mode 100644 index 0000000..b0fc3bc --- /dev/null +++ b/exercises/smelodesousa/F5/5-absolute-majority/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Absolute majority!", + "identifier" : "5.12", + "authors" : [["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-zombie-attack"] +} diff --git a/exercises/smelodesousa/F5/5-absolute-majority/prelude.ml b/exercises/smelodesousa/F5/5-absolute-majority/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-absolute-majority/prepare.ml b/exercises/smelodesousa/F5/5-absolute-majority/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-absolute-majority/solution.ml b/exercises/smelodesousa/F5/5-absolute-majority/solution.ml new file mode 100644 index 0000000..abac2c8 --- /dev/null +++ b/exercises/smelodesousa/F5/5-absolute-majority/solution.ml @@ -0,0 +1,52 @@ +(* 1 *) +let check_voter hashtable n number_candidates = + try ( let count = Hashtbl.find hashtable n in + Hashtbl.replace hashtable n (count+1);) + with Not_found -> + ( Hashtbl.add hashtable n 1; + number_candidates := !number_candidates + 1;) + +let majority array_votos = + let my_hash = Hashtbl.create 10 in + let number_candidates = ref(0) in + let majr = ref(0) in + let number = ref (0) in + let majority_candidate_list = ref [] in + begin + for i=0 to (Array.length array_votos) - 1 do + check_voter my_hash (array_votos.(i)) number_candidates; + done; + for i=1 to !number_candidates do + number := Hashtbl.find my_hash i; + if !majr = !number then majority_candidate_list := !majority_candidate_list @ [i] + else if !majr < !number then ( majr := !number; majority_candidate_list := [i] ) + else (); + done; + if (List.length !majority_candidate_list) >= 2 then raise Not_found + else List.nth !majority_candidate_list 0; + end + +(* 2 *) +let mjrty (a: ((int) list)) : int = + let exception QtReturn of (int) in + try + let n = List.length a in + let cand = ref (List.nth a 0) in + let k = ref 1 in + (let o = n - 1 in let o1 = 1 in + for i = o1 to o do + if !k = 0 + then begin cand := List.nth a i; k := 1 end + else begin if !cand = List.nth a i then incr k else decr k end + done); + if !k = 0 then raise (Not_found); + if !k > n / 2 then raise (QtReturn !cand); + k := 0; + (let o = n - 1 in let o1 = 0 in + for i1 = o1 to o do + if List.nth a i1 = !cand + then begin incr k; if !k > n / 2 then raise (QtReturn !cand) end + done); + raise (Not_found) + with + | QtReturn r -> r \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-absolute-majority/template.ml b/exercises/smelodesousa/F5/5-absolute-majority/template.ml new file mode 100644 index 0000000..8d9d18b --- /dev/null +++ b/exercises/smelodesousa/F5/5-absolute-majority/template.ml @@ -0,0 +1,3 @@ +let majority votes = failwith "Replace with your solution" + +let mjrty l = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-absolute-majority/test.ml b/exercises/smelodesousa/F5/5-absolute-majority/test.ml new file mode 100644 index 0000000..a09cf32 --- /dev/null +++ b/exercises/smelodesousa/F5/5-absolute-majority/test.ml @@ -0,0 +1,35 @@ +open Test_lib +open Report +open List +open Random + +let maioriaT = Section( + [Text "Testing majority function"], + test_function_1_against_solution + [%ty: int array -> int] + "majority" + ~sampler: (sample_array ~min_size: 32 ~max_size: 32 ~dups: true (fun () -> Random.int(3) + 1)) + ~gen: 5 + [[|1; 1; 2; 1; 2; 3; 3; 2; 2; 2; 1; 2; 2; 3; 2; 2; 1; 1; 2; 1; 2; 3; 3; 2; 2; 2; 1; 2; 2; 3; 2; 2|]; + [|1; 3; 3; 3; 1; 2; 2; 3; 3; 3; 1; 1; 3; 3; 1; 2; 1; 3; 3; 3; 1; 2; 2; 3; 3; 3; 1; 1; 3; 3; 1; 2|]; + [|1; 2; 2; 1; 1; 1; 1; 3; 3; 1; 2; 1; 1; 3; 1; 3; 1; 2; 2; 1; 1; 1; 1; 3; 3; 1; 2; 1; 1; 3; 1; 3|]; + [|3; 1; 2; 1; 2; 3; 3; 3; 2; 2; 1; 3; 2; 1; 3; 3; 3; 1; 2; 1; 2; 3; 3; 3; 2; 2; 1; 3; 2; 1; 3; 3|]; + [|1; 1; 2; 1; 2; 3; 3; 2; 2; 2; 1; 2; 2; 3; 2; 2; 1; 1; 2; 1; 2; 3; 3; 2; 3; 3; 3; 3; 3; 3; 3; 1|]]) + +let mjrtyT = Section( + [Text "Testing mjrty function"], + test_function_1_against_solution + [%ty: int list -> int] + "mjrty" + ~sampler: (sample_list ~min_size: 32 ~max_size: 32 ~dups: true (fun () -> Random.int(3) + 1)) + ~gen: 10 + [[1; 1; 2; 1; 2; 3; 3; 2; 2; 2; 1; 2; 2; 3; 2; 2; 1; 1; 2; 1; 2; 3; 3; 2; 2; 2; 1; 2; 2; 3; 2; 2]; + [1; 3; 3; 3; 1; 2; 2; 3; 3; 3; 1; 1; 3; 3; 1; 2; 1; 3; 3; 3; 1; 2; 2; 3; 3; 3; 1; 1; 3; 3; 1; 2]; + [1; 2; 2; 1; 1; 1; 1; 3; 3; 1; 2; 1; 1; 3; 1; 3; 1; 2; 2; 1; 1; 1; 1; 3; 3; 1; 2; 1; 1; 3; 1; 3]; + [3; 1; 2; 1; 2; 3; 3; 3; 2; 2; 1; 3; 2; 1; 3; 3; 3; 1; 2; 1; 2; 3; 3; 3; 2; 2; 1; 3; 2; 1; 3; 3]; + [1; 1; 2; 1; 2; 3; 3; 2; 2; 2; 1; 2; 2; 3; 2; 2; 1; 1; 2; 1; 2; 3; 3; 2; 3; 3; 3; 3; 3; 3; 3; 1]]) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [maioriaT;mjrtyT] \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-brackets/descr.md b/exercises/smelodesousa/F5/5-brackets/descr.md new file mode 100644 index 0000000..cac4b4b --- /dev/null +++ b/exercises/smelodesousa/F5/5-brackets/descr.md @@ -0,0 +1,20 @@ +# Introduction + +Having a text with well-placed parentheses, in other words, with the symbols "(" and ")" properly used, is a common necessity, and of great importance when these texts are subject to computer processing (think of a compiler, for example). + +In this exercise, we challenge you to check the proper use of parentheses in any text organized in the form of a list of characters. + +A text has well-placed parentheses if any occurrence of '(': + +- is associated with an occurrence of ')' *at the right*; +- also has well-placed parentheses in the text between itself and the corresponding ')'. + +Note that, as a result, concatenating two texts with well-placed parentheses or putting a text with well-placed parentheses between parentheses also results in a text with correctly placed parentheses. + +# Goal + +Define a function `verify : char list -> char list -> bool` which checks if the text contained in the first parameter has well-placed parentheses. It is suggested that you use the second parameter (another `char list`, which we will define the accumulator) to _accumulate_ the intermediate scan results. Therefore, the accumulator is empty at the beginning of the analysis, and if during the check the accumulator has a parenthesis '(' as its first element, then the current status of the analysis still waits for a parenthesis ')' which corresponds to the closing of the parenthesis in the accumulator. A proper use of this accumulator makes checking much easier! + +For example: + +`verify ['a';'(';'a';'b';'(';'b';')';'c';'(';'o';'k';'a';')';'n';')';'h'] [] = true` \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-brackets/meta.json b/exercises/smelodesousa/F5/5-brackets/meta.json new file mode 100644 index 0000000..c5850a0 --- /dev/null +++ b/exercises/smelodesousa/F5/5-brackets/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 3, + "title" : "Correctly using parentheses", + "identifier" : "5.20", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], + "backward_exercises": ["smelodesousa/F5/5-subsequence-of-lists"] +} diff --git a/exercises/smelodesousa/F5/5-brackets/prelude.ml b/exercises/smelodesousa/F5/5-brackets/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-brackets/prepare.ml b/exercises/smelodesousa/F5/5-brackets/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-brackets/solution.ml b/exercises/smelodesousa/F5/5-brackets/solution.ml new file mode 100644 index 0000000..fba3885 --- /dev/null +++ b/exercises/smelodesousa/F5/5-brackets/solution.ml @@ -0,0 +1,12 @@ +let verify text accumulator = + let clean_accumulator acc = + match acc with + | c1::c2::t -> if c1 = ')' && c2 = '(' then t else acc + | c1::[] -> acc + | [] -> acc in + let rec verify_aux text accumulator = + match text with + | c::t when (c = '(' || c = ')') -> verify_aux t (clean_accumulator (c::accumulator)) + | c::t when (c <> '(' || c <> ')') -> verify_aux t accumulator + | [] -> accumulator in + (verify_aux text accumulator) = [] \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-brackets/template.ml b/exercises/smelodesousa/F5/5-brackets/template.ml new file mode 100644 index 0000000..8be3ac5 --- /dev/null +++ b/exercises/smelodesousa/F5/5-brackets/template.ml @@ -0,0 +1,2 @@ +let verify text accumulator = + failwith "Replace with your solution" diff --git a/exercises/smelodesousa/F5/5-brackets/test.ml b/exercises/smelodesousa/F5/5-brackets/test.ml new file mode 100644 index 0000000..2aaf5d7 --- /dev/null +++ b/exercises/smelodesousa/F5/5-brackets/test.ml @@ -0,0 +1,26 @@ +open Test_lib +open Report + +let sample_verify_right () = + ([], []) +let sample_verify_wrong () = + ([], []) + +let sample_verify () = + let () = Random.self_init () in + if (Random.int 2) = 0 + then sample_verify_right () + else sample_verify_wrong () + +let verifyS () = + test_function_2_against_solution + [%ty: char list -> char list -> bool ] + "verify" + ~sampler: sample_verify + ~gen: 0 + [(['a';'(';'a';'b';'(';'b';')';'c';'(';'o';'k';'a';')';'n';')';'h'], []); (['a';'(';'a';'b';'(';'b';')';'c';'(';'o';'k';'a';'n';')';'h'], [])] + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + verifyS () diff --git a/exercises/smelodesousa/F5/5-burrows-wheeler/descr.md b/exercises/smelodesousa/F5/5-burrows-wheeler/descr.md new file mode 100644 index 0000000..7b80e4f --- /dev/null +++ b/exercises/smelodesousa/F5/5-burrows-wheeler/descr.md @@ -0,0 +1,62 @@ +# Introduction + +The **Burrows-Wheeler transform (BWT)** is a pretreatment process for data compression, invented by Michael Burrows and David Wheeler in 1984 (after the first results by D. Wheeler). It is not a compression algorithm as it does not reduce the size of the processed text, but _BWT_ has the property of calculating permutations of the text that group together similar characters. These groupings make the resulting text a particularly interesting candidate for _RLE_ type methods (look at the exercise in this sheet). + +It is a technique used in compression systems such as _bzip2_ or in the area of computational genomics, where it finds applications in finding sequence alignment or repeatitions. + +More details at: + +- Michael Burrows, D. J. Wheeler: ["A block-sorting lossless data compression algorithm"](http://citeseer.ist.psu.edu/76182.html), 10th May 1994, Digital SRC Research Report 124. + +- [Article by Dr. Dobb's on Burrows-Wheeler](http://marknelson.us/1996/09/01/bwt/) + +# Objectives + +The purpose of this exercise is to implement the encoding and decoding processes. To do so, we will illustrate the process with a complete example for each one. + +We intend to encode with BWT the word "ANAGRAMA". + +Encoding: + +First, we create a word-sized square character matrix of the uncoded word. This matrix is filled by doing a right rotating _shift_. + + +```pseudocode + matrix + +A N A G R A M A +A A N A G R A M +M A A N A G R A +A M A A N A G R +R A M A A N A G +G R A M A A N A +A G R A M A A N +N A G R A M A A +``` + +Then, you sort the rows of this matrix alphabetically. + +```pseudocode + matrix # line + +A A N A G R A M 1 +A G R A M A A N 2 +A M A A N A G R 3 +A N A G R A M A 4 +G R A M A A N A 5 +M A A N A G R A 6 +N A G R A M A A 7 +R A M A A N A G 8 +``` + + + +The encoding will be the pair `(4, "MNRAAAAG")`. The 4 is the line number where the original word is in the sorted array. The word "MNRAAAG" is the word made up of the letters in the last column, from top to bottom. + +The decoding starts at `(4,"MNRAAAAG")` and finds the word "ANAGRAMA" without the knowledge of this matrix. + +- Write a function `bwt: string -> int*string` that encodes a word with the BWT method. + + Therefore, `bwt "anagrama" = (4,"mnraaaag")`. + +- Write a function `debwt : int * string -> string`. For example, `debwt (4,"mnraaag") = "anagrama"`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-burrows-wheeler/meta.json b/exercises/smelodesousa/F5/5-burrows-wheeler/meta.json new file mode 100644 index 0000000..f41a488 --- /dev/null +++ b/exercises/smelodesousa/F5/5-burrows-wheeler/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 3, + "title": "Burrows-Wheeler transform", + "identifier": "5.18", + "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], + "backward_exercises": ["mooc/week3/seq3/ex1"] +} diff --git a/exercises/smelodesousa/F5/5-burrows-wheeler/prelude.ml b/exercises/smelodesousa/F5/5-burrows-wheeler/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-burrows-wheeler/prepare.ml b/exercises/smelodesousa/F5/5-burrows-wheeler/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-burrows-wheeler/solution.ml b/exercises/smelodesousa/F5/5-burrows-wheeler/solution.ml new file mode 100644 index 0000000..19d4e15 --- /dev/null +++ b/exercises/smelodesousa/F5/5-burrows-wheeler/solution.ml @@ -0,0 +1,124 @@ +(* 1 + let max_word = 1000;; + + let (<<) f g x = f (g x);; + + let list_of_string l = + let temp = ref [] in + for i=0 to (String.length l) -1 do temp := l.[i]::!temp + done; List.rev !temp;; + + let with_nth_char m c = + String.mapi (fun i b -> if i = m then c else b);; + + let listachar_para_string lista = + let tamanho = List.length lista in + let resultado = ref (String.make tamanho '0') in + let rec aux i = + if i >= tamanho then !resultado + else (resultado := with_nth_char i (List.nth lista i) !resultado; aux (succ i)) in + aux 0;; + + let rec last = function [] -> failwith "too short list..." | [el] -> el + | el::li -> last li ;; + + let rec do_n n f l acc= + if n<=0 then acc + else let nl = f l in do_n (n-1) f nl (nl::acc);; + + let lexsort = List.sort compare;; + + let lrot = function [] -> [] | el:: li -> li@[el];; + + let rotations p = let n = List.length p in do_n n lrot p [];; + + let bwp = (List.map last) << lexsort << rotations;; + + let rec bwn_aux p r v = + match r with [] -> failwith "bwn -> where is the word????" + | pp :: li -> if p=pp then v else (bwn_aux p li (v+1));; + + let bwn p = bwn_aux p ((lexsort << rotations) p) 0;; + + let bwt word = + ((bwn (list_of_string(word)) + 1), listachar_para_string (bwp (list_of_string(word))));; + + (* 2 *) + + let index_table l n htbl = + for i = n-1 downto 0 do + Hashtbl.add htbl l.(i) i + done;; + + let with_nth_char m c = + String.mapi (fun i b -> if i = m then c else b);; + + let rosetta (last: char array) = + let n = (Array.length last) in + let r = Array.make n 0 in + let first = Array.copy last in + let _ = (Array.sort compare first) in + let indtbl = Hashtbl.create n in + let _ = (index_table first n indtbl) in + for i=0 to (n-1) do + let c = last.(i) in + r.(i) <- Hashtbl.find indtbl c; + Hashtbl.remove indtbl c + done; r;; + + let unbwt (last: char array) rank rosetta = + let n = Array.length last in + let original = ref (String.make n '0') in + let index = ref rank in + for i = n-1 downto 0 do + original := with_nth_char i (last.(!index)) !original; + index := rosetta.(!index) + done; !original;; + + let to_array (s:string) = + Array.init (String.length s) (String.get s);; + + let debwt input_pair = + match input_pair with + |(a,b) -> + let last,n = (to_array b,a) in + (unbwt last (n-1) (rosetta last));; *) + +(* Another possible solution *) + +(* 1 *) +let bwt word = + (* table with all possible rotations of word *) + let table = + Array.init (String.length word) (fun i -> + String.sub word i (String.length word - i) ^ String.sub word 0 i) + in + (* sort the table *) + Array.sort compare table; + (* get the line where the word is *) + let line = ref 0 in + while table.(!line) <> word do + incr line + done; + (* join all chars from the last column of the matrix *) + let result = Bytes.create (String.length word) in + for i = 0 to String.length word - 1 do + Bytes.set result i table.(i).[String.length word - 1] + done; + (* return the result in the appropriate form*) + (!line + 1, Bytes.to_string result) + +(* 2 *) +let debwt input_pair = + let line, word = input_pair in + (* create empty table *) + let table = Array.make (String.length word) "" in + (* insert s as a column of table before first column of the table, and repeat n times *) + for i = 0 to String.length word - 1 do + for j = 0 to String.length word - 1 do + table.(j) <- String.make 1 word.[j] ^ table.(j) + done; + Array.sort compare table + done; + (* return the line-th row of the table *) + table.(line - 1) diff --git a/exercises/smelodesousa/F5/5-burrows-wheeler/template.ml b/exercises/smelodesousa/F5/5-burrows-wheeler/template.ml new file mode 100644 index 0000000..35299de --- /dev/null +++ b/exercises/smelodesousa/F5/5-burrows-wheeler/template.ml @@ -0,0 +1,2 @@ +let bwt word = failwith "Unanswered" +let debwt input_pair = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-burrows-wheeler/test.ml b/exercises/smelodesousa/F5/5-burrows-wheeler/test.ml new file mode 100644 index 0000000..f99eed8 --- /dev/null +++ b/exercises/smelodesousa/F5/5-burrows-wheeler/test.ml @@ -0,0 +1,34 @@ +open Test_lib +open Report +open List +open Solution + +let rec generate_word n = + String.concat "" + (map + (fun _ -> String.make 1 (char_of_int (97 + Random.int 26))) + (init n (fun _ -> ()))) + +let bwtT = + Section + ( [ Text "Testing function"; Code "bwt" ], + test_function_1_against_solution [%ty: string -> int * string] "bwt" + ~sampler:(fun () -> generate_word (Random.int 4 + 5)) + ~gen:10 + [ "anagrama"; "abraca"; "yokohama"; "tototo"; "mosaissova" ] ) + +let debwtT = + Section + ( [ Text "Testing function"; Code "debwt" ], + test_function_1_against_solution [%ty: int * string -> string] "debwt" + ~sampler:(fun () -> bwt (generate_word (Random.int 4 + 5))) + ~gen:10 + [ + (2, "caraab"); + (8, "hmooakya"); + (4, "tttooo"); + (4, "svaamsosio"); + (4, "mnraaaag"); + ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ bwtT; debwtT ] \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-gray-codes/descr.md b/exercises/smelodesousa/F5/5-gray-codes/descr.md new file mode 100644 index 0000000..41c5efb --- /dev/null +++ b/exercises/smelodesousa/F5/5-gray-codes/descr.md @@ -0,0 +1,61 @@ + + + + + +# Introduction + +Gray codes allow for a good binary codification, in which only one bit changes between two consecutive elements. + +To simplify the problem, let's only think about integers. In this case, the codification of $0$ is $0$, and $1$ is $1$. The codification of $17$ is $11001$, $18$ is $11011$ and $19$ is $11010$. + +A simple way of generating gray codes from integer values up to length $n$ (for example $19$ has a length of $5$) is the *reflex-and-prefix* method. + +The definition of the method is as follows: + +```pseudocode +Base reflex/prefix n.1 reflex/prefix n.2 reflex/prefix n.3 +0 0 0 *0 0 00 0 *00 0 000 0 *000 0 0000 +1 1 1 *1 1 01 1 *01 1 001 1 *001 1 0001 + reflex prefix 2 *11 2 011 2 *011 2 0011 + 2 *1 2 11 3 *10 3 010 3 *010 3 0010 + 3 *0 3 10 reflex prefix 4 *110 4 0110 + 4 *10 4 110 5 *111 5 0111 + 5 *11 5 111 6 *101 6 0101 + 6 *01 6 101 7 *100 7 0100 + 7 *00 7 100 reflex prefix + 8 *100 8 1100 + 9 *101 9 1101 + 10 *111 10 1111 + 11 *110 11 1110 + 12 *010 12 1010 + 13 *011 13 1011 + 14 *001 14 1001 + 15 *000 15 1000 +``` + +# Goals + +1. Define the function `gray_list int -> string list` that given an $n$ value calculates every gray code of length $n$. These codes are returned as a string list.
+If the argument is invalid, the exception `Invalid_argument "gray_list"` is thrown.
+For example `gray_list 2 = ["000";"001";"011";"010";"110";"111";"101";"100"]`. + +2. Define the function `gray_code : int -> string` that returns the gray code of a certain $n$ integer parameter as a string. + +3. Define the function `gray : int -> int` that calculates the gray codification of the positive integer passed as a parameter.
+If the argument is invalid, the exception `Invalid_argument "gray"` is thrown.
+For example `gray 9 = 13` (13 = 1101 in binary). + +4. Define the function `de_gray : int -> int` that does the inverse operation, the decodification.
+If the argument is invalid, the exception `Invalid_argument "de_gray"` is thrown.
+For example, `de_gray 13 = de_gray 0b1101 = 9` (`0b1101` is 13 in binary notation). + +**Note**: Feel free to define other functions to solve the problem. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-gray-codes/meta.json b/exercises/smelodesousa/F5/5-gray-codes/meta.json new file mode 100644 index 0000000..2abfa44 --- /dev/null +++ b/exercises/smelodesousa/F5/5-gray-codes/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Gray codes", + "identifier" : "5.4", + "authors" : [["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-holand"] +} diff --git a/exercises/smelodesousa/F5/5-gray-codes/prelude.ml b/exercises/smelodesousa/F5/5-gray-codes/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-gray-codes/prepare.ml b/exercises/smelodesousa/F5/5-gray-codes/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-gray-codes/solution.ml b/exercises/smelodesousa/F5/5-gray-codes/solution.ml new file mode 100644 index 0000000..21fda64 --- /dev/null +++ b/exercises/smelodesousa/F5/5-gray-codes/solution.ml @@ -0,0 +1,56 @@ +(* 1 *) +let gray_list n = + if n < 0 then raise(Invalid_argument "gray_list"); + let n1 = n + 1 in + let rec gray_next_level k l = + if k + (("0"^x)::acc1, ("1"^x)::acc2 )) ([],[]) l + in + gray_next_level (k+1) (List.rev_append first_half second_half) + else l + in + gray_next_level 1 ["0"; "1"];; + +(* 2 *) +let dec_to_bin x = + let rec d2b y lst = match y with 0 -> lst + | _ -> d2b (y/2) ((y mod 2)::lst) + in + d2b x [];; + +let with_nth_char m c = + String.mapi (fun i b -> if i = m then c else b);; + +let replace i = + if i = 0 then '0' else '1';; + +let replace_opt i = + match i with + | None -> '0' + | Some a -> if a = 0 then '0' else '1';; + +let gray_code n = + let binary = dec_to_bin n in + let size = List.length (binary) in + let result = ref (String.make size '0') in + let rec aux i = + if i = 0 then result := (with_nth_char i (replace_opt (List.nth_opt binary i)) !result) + else result := (with_nth_char (i) (replace ((List.nth binary (i-1)) lxor (List.nth binary (i)))) !result); + if i >= (size - 1) then if n = 0 then "0" else !result else aux (succ i) in + aux 0;; + +(* 3 *) +let gray b = + if b < 0 then raise (Invalid_argument "gray"); + b lxor (b lsr 1) + +(* 4 *) +let de_gray n = + if n < 0 then raise (Invalid_argument "de_gray"); + let rec aux p n = + if n = 0 then p + else aux (p lxor n) (n lsr 1) + in + aux n (n lsr 1) \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-gray-codes/template.ml b/exercises/smelodesousa/F5/5-gray-codes/template.ml new file mode 100644 index 0000000..0bb448b --- /dev/null +++ b/exercises/smelodesousa/F5/5-gray-codes/template.ml @@ -0,0 +1,7 @@ +let gray_list n = failwith "Replace with your solution" + +let gray_code x = failwith "Replace with your solution" + +let gray b = failwith "Replace with your solution" + +let de_gray n = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-gray-codes/test.ml b/exercises/smelodesousa/F5/5-gray-codes/test.ml new file mode 100644 index 0000000..0583644 --- /dev/null +++ b/exercises/smelodesousa/F5/5-gray-codes/test.ml @@ -0,0 +1,46 @@ +open Test_lib +open Report +open List +open Random + + +let gray_listT = Section ( + [Text "Testing gray_list function"], + test_function_1_against_solution + [%ty: int -> string list] + "gray_list" + ~sampler: (fun () -> let () = Random.self_init () in Random.int(7)) + ~gen: 10 + [(-1);10;(-10)]) + +let gray_codeT = Section ( + [Text "Testing gray_code function"], + test_function_1_against_solution + [%ty: int -> string] + "gray_code" + ~sampler: (fun () -> let () = Random.self_init () in Random.int(115)) + ~gen: 10 + []) + +let grayT = Section ( + [Text "Testing gray function"], + test_function_1_against_solution + [%ty: int -> int] + "gray" + ~sampler: (fun () -> let () = Random.self_init () in Random.int(15)) + ~gen: 10 + [(-1);10;(-10)]) + +let de_grayT = Section ( + [Text "Testing de_gray function"], + test_function_1_against_solution + [%ty: int -> int] + "de_gray" + ~sampler: (fun () -> let () = Random.self_init () in Random.int(15)) + ~gen: 10 + [(-1);10;(-10)]) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [gray_listT; gray_codeT; grayT; de_grayT] \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-half/descr.md b/exercises/smelodesousa/F5/5-half/descr.md new file mode 100644 index 0000000..6a7f12d --- /dev/null +++ b/exercises/smelodesousa/F5/5-half/descr.md @@ -0,0 +1,7 @@ +# Objective + +Define the function `halve : int list -> (int list * int list)` that takes in a list as an argument and cuts it in half. In the case of an odd-length list, the middle element stays on the rightmost list. + +As an example, `halve [1;2;3;4;5;6;7;8;9] = ([1;2;3;4],[5;6;7;8;9])`. + +It is important to note that there is a solution that uses an auxiliary tail recursive function (with the same parameter configuration as `halve`), that only needs to run through the complete list and its first half once. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-half/meta.json b/exercises/smelodesousa/F5/5-half/meta.json new file mode 100644 index 0000000..aa37084 --- /dev/null +++ b/exercises/smelodesousa/F5/5-half/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "In case of litigation, split it in half", + "identifier" : "5.5", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-salc"] +} diff --git a/exercises/smelodesousa/F5/5-half/prelude.ml b/exercises/smelodesousa/F5/5-half/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-half/prepare.ml b/exercises/smelodesousa/F5/5-half/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-half/solution.ml b/exercises/smelodesousa/F5/5-half/solution.ml new file mode 100644 index 0000000..98ff2d1 --- /dev/null +++ b/exercises/smelodesousa/F5/5-half/solution.ml @@ -0,0 +1,8 @@ +let halve l = + let len = List.length l in + let rec halve_aux (acc, acc2) i = function + | [] -> (acc |> List.rev, acc2 |> List.rev) + | x :: xs -> if i < (len / 2) then halve_aux ((x :: acc), acc2) (i + 1) xs + else halve_aux (acc, (x :: acc2)) (i + 1) xs + in + halve_aux ([], []) 0 l diff --git a/exercises/smelodesousa/F5/5-half/template.ml b/exercises/smelodesousa/F5/5-half/template.ml new file mode 100644 index 0000000..01cc1f9 --- /dev/null +++ b/exercises/smelodesousa/F5/5-half/template.ml @@ -0,0 +1,2 @@ +let halve l = + failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-half/test.ml b/exercises/smelodesousa/F5/5-half/test.ml new file mode 100644 index 0000000..0069be3 --- /dev/null +++ b/exercises/smelodesousa/F5/5-half/test.ml @@ -0,0 +1,15 @@ +open Test_lib +open Report + +let halveS () = + test_function_1_against_solution + [%ty: int list -> (int list * int list)] + "halve" + ~sampler: (sample_list ~min_size: 10 ~max_size: 50 (fun () -> let () = Random.self_init () in (Random.int 50))) + ~gen: 10 + [([]); [1]] + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + halveS () diff --git a/exercises/smelodesousa/F5/5-holand/descr.md b/exercises/smelodesousa/F5/5-holand/descr.md new file mode 100644 index 0000000..f25d21a --- /dev/null +++ b/exercises/smelodesousa/F5/5-holand/descr.md @@ -0,0 +1,58 @@ + + + + + +# Introduction + +This programming problem was originally proposed by [Edsger Dijkstra](https://en.wikipedia.org/wiki/Edsger_Dijkstra) to illustrate and highlight some expected properties of a sorting algorithm, such as stability. + +Given an arbitrary sequence of 3 colored balls of arbitrary length. How to sort this sequence so that the sequence is ordered (the blue balls first, followed by the white balls, and finally the red balls)? Moreover, it is intended that the original order of balls of the same color is respected! + +For instance, in the unordered sequence, if a particular blue ball is in a more left-handed position than another blue ball, then in the ordered sequence, this order holds. It remains in a more left-handed position than the other, and the ordering is said to be *stable*. + +The algorithm proposed by Dijkstra for this problem shows that it is possible to sort a collection of $n$ colored objects using a *linear* number of color comparisons. Although, classical sorting algorithms (the family of sorting algorithms that do not use the information particular about objects for sorting, for example, here the knowledge that there are only three colors, blue, white, and red) needs on average (and in the worst case in the best algorithms) a greater number of comparisons, on the order of $n .log(n)$ + +We will assume the OCaml type to represent the colors and the following utility functions: + +```ocaml +type color_names = Blue | White | Red +type ball = (color_names*int) +let color ((c,_) : ball) = c +let index ((_,i) : ball) = i +``` + +Then, we propose this pseudocode for the desired sorting. Note that this pseudocode *purposely has "subtle" errors*! + +```pseudocode +input: a: vector of ball elements + +b := 0 +i := 0 +r := length of a + +When i < r do + if color a[i] = Blue then + swap the values of a[b] e a[i] + increment b + else if cor a[i] = White then + increment i + else // color a[i] = Red + increment r + swap the values of a[r] e a[i] + end do +``` + +# Objetive + +After executing this pseudocode on paper and correcting any errors it contains, define the function `dutch_flag : ball array -> ball array` that sorts the parameter vector using this algorithm. Assigning an index to each color using the pair `(color_names * int)` enables us to determine whether the suggested approach is stable. + +Therefore: `dutch_flag [|(Red,0);(White,1);(Blue,2);(Red,3);(Blue,4);(White,5);(Blue,6);(Red,7);(White,8);(Blue,9)|] = [(Blue,2);(Blue,4);(Blue,6);(Blue,9);(White,1);(White,5);(White,8);(Red,0);(Red,3);(Red,7)|] ` \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-holand/meta.json b/exercises/smelodesousa/F5/5-holand/meta.json new file mode 100644 index 0000000..bf648f0 --- /dev/null +++ b/exercises/smelodesousa/F5/5-holand/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "The dutch flag problem", + "identifier": "5.16", + "authors": [["Rui Barata", "rui.barata@ubi.pt"]], + "backward_exercises": ["smelodesousa/F5/5-absolute-majority"] +} diff --git a/exercises/smelodesousa/F5/5-holand/prelude.ml b/exercises/smelodesousa/F5/5-holand/prelude.ml new file mode 100644 index 0000000..525d3bd --- /dev/null +++ b/exercises/smelodesousa/F5/5-holand/prelude.ml @@ -0,0 +1,5 @@ +type color_names = Blue | White | Red +type ball = color_names * int + +let color ((c, _) : ball) = c +let index ((_, i) : ball) = i diff --git a/exercises/smelodesousa/F5/5-holand/prepare.ml b/exercises/smelodesousa/F5/5-holand/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-holand/solution.ml b/exercises/smelodesousa/F5/5-holand/solution.ml new file mode 100644 index 0000000..080de23 --- /dev/null +++ b/exercises/smelodesousa/F5/5-holand/solution.ml @@ -0,0 +1,21 @@ +(* let dutch_flag a = + let l = Array.to_list a in + let b = ref [] in + let w = ref [] in + let r = ref [] in + let rec dutch_flag_aux a = + match a with + | h::t -> (match (color h) with | Blue -> (b := !b@[h]; dutch_flag_aux t) | White -> (w := !w@[h]; dutch_flag_aux t) | Red -> (r := !r@[h]; dutch_flag_aux t)) + | [] -> () in + begin + dutch_flag_aux l; + Array.of_list ((!b @ !w) @ !r) + end *) + +(* Another possible solution *) +let dutch_flag a = + Array.sort + (fun x y -> + (compare (color x) (color y) * 10) + compare (index x) (index y)) + a; + a \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-holand/template.ml b/exercises/smelodesousa/F5/5-holand/template.ml new file mode 100644 index 0000000..b778134 --- /dev/null +++ b/exercises/smelodesousa/F5/5-holand/template.ml @@ -0,0 +1 @@ +let dutch_flag a = failwith "Unanswered" diff --git a/exercises/smelodesousa/F5/5-holand/test.ml b/exercises/smelodesousa/F5/5-holand/test.ml new file mode 100644 index 0000000..90466eb --- /dev/null +++ b/exercises/smelodesousa/F5/5-holand/test.ml @@ -0,0 +1,44 @@ +open Test_lib +open Report + +let index = ref (-1) + +let sample_element () = + let () = Random.self_init () in + match Random.int 3 with + | 0 -> + index := !index + 1; + (Blue, !index) + | 1 -> + index := !index + 1; + (White, !index) + | 2 -> + index := !index + 1; + (Red, !index) + +let sample_dutch_flag () = + index := -1; + sample_array ~min_size:5 ~max_size:15 sample_element () + +let dutch_FlagS = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "dutch_flag" ], + test_function_1_against_solution [%ty: ball array -> ball array] + "dutch_flag" ~sampler:sample_dutch_flag ~gen:9 + [ + [| + (Red, 0); + (White, 1); + (Blue, 2); + (Red, 3); + (Blue, 4); + (White, 5); + (Blue, 6); + (Red, 7); + (White, 8); + (Blue, 9); + |]; + ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ dutch_FlagS ] diff --git a/exercises/smelodesousa/F5/5-horner/descr.md b/exercises/smelodesousa/F5/5-horner/descr.md new file mode 100644 index 0000000..365e419 --- /dev/null +++ b/exercises/smelodesousa/F5/5-horner/descr.md @@ -0,0 +1,46 @@ + + + + + +# Introduction +We can represent a polynomial $P$ of degree $n$ with a list $p$ of real numbers, where its $i$th element represents the coefficient associated with the exponent of degree $i$. + +Thus, the polynomial $3x^4+5x^2+1$, for example, is represented by the list `[3;0;5;0;1]` (or `[1;0;5;0;3]`, if we prefer to list the polynomial from the smallest degree to the highest). In this exercise, we will assume that the highest degree is always at the left. + +# Objectives + +1. Choose the option that best defines the type `polynomial` as a pair of an integer number that represents the highest degree of the polynomial and a list of real numbers. Using the example from above, its value of type `polynomial` would be `(4, [3.; 0.; 5.; 0.; 1.])`. Note that the integer representing the highest degree is a non-negative integer. In other words, the list cannot be empty, otherwise, a polynomial wouldn't exist. + + A) `type polynomial = { degree : int; polynomial : float list }`
+ B) `type polynomial = (int * (float list))`
+ C) `type polynomial = ((float list) * int)`
+ D) `type polynomial = { polynomial : float list; degree : int }`
+ E) `type polynomial = (float * (int list))`
+ F) `type polynomial = { polynomial : int list; degree : float }`

+ + (Note: If you believe the correct option is `A`, then you should answer as follows: `let answer = A`) + +2. Implement a function `horner : float -> polynomial -> float` that, given a real number $x$, determines $P(x)$ by using Horner's method, i.e. + +
+ $P_n(x)=(\cdots((a_n x + a_{n-1})x + a_{n-2})x + \cdots + a_1)x + a_0$ +
+ + Thus, `horner 3.0 (4,[3.; 0.; 5.; 0.; 1.])` returns `289.0`. In case of an invalid argument, the exception `Invalid_argument "horner"` may be thrown. + +3. Implement a function `derivative : polynomial -> polynomial` that, given a polynomial $P(x)$ in the form of a list, determines its respective derivative, which is also a polynomial. In case of an invalid argument, the exception `Invalid_argument "horner"` may be thrown. + + For example, `derivative (4,[3.; 0.; 5.; 0.; 1.])` returns `(3,[12.; 0.; 10.; 0.])`. + +4. Create a tail recursive version `derivative_tr : polynomial -> polynomial -> polynomial` of the previous function `derivative`. Note: the function will be tested in the following manner: `derivative_tr p (-1, [])`. + + For example, `derivative_tr (4,[3.; 0.; 5.; 0.; 1.]) (-1, [])` returns `(3,[12.; 0.; 10.; 0.])`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-horner/meta.json b/exercises/smelodesousa/F5/5-horner/meta.json new file mode 100644 index 0000000..d5cde7d --- /dev/null +++ b/exercises/smelodesousa/F5/5-horner/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Operations on single variable Polynomials - Horner's method and derivation", + "identifier": "5.6", + "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-half"] +} diff --git a/exercises/smelodesousa/F5/5-horner/prelude.ml b/exercises/smelodesousa/F5/5-horner/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-horner/prepare.ml b/exercises/smelodesousa/F5/5-horner/prepare.ml new file mode 100644 index 0000000..754662f --- /dev/null +++ b/exercises/smelodesousa/F5/5-horner/prepare.ml @@ -0,0 +1,4 @@ +type polynomial = (int*(float list)) + +type choice = + | A | B | C | D | E | F | Unanswered \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-horner/solution.ml b/exercises/smelodesousa/F5/5-horner/solution.ml new file mode 100644 index 0000000..64546c6 --- /dev/null +++ b/exercises/smelodesousa/F5/5-horner/solution.ml @@ -0,0 +1,61 @@ +(* 1 *) +(* Multiple-choice question *) +let answer = B + +(* 2 *) +let horner x p = + let rec aux x ret = function + | h :: t -> aux x ((ret *. x) +. h) t + | [] -> ret + in + match p with + | d, h :: t when d = List.length t -> aux x h t + | _ -> raise (Invalid_argument "horner") + +(* 3 *) +let derivative p = + let rec aux n ret = function + | [ _ ] -> List.rev ret + | h :: t -> aux (n -. 1.) ((h *. n) :: ret) t + | [] -> raise (Invalid_argument "horner") + in + match p with + | d, pol when d = List.length pol - 1 -> (d - 1, aux (float_of_int d) [] pol) + | _ -> raise (Invalid_argument "horner") + +(* 4 *) +let rec derivative_tr p acc = + match acc with + | d1, p1 -> ( + match p with + | d2, pol when d2 <> List.length pol - 1 -> + raise (Invalid_argument "horner") + | _, [] -> raise (Invalid_argument "horner") + | _, [ _ ] -> (d1, List.rev p1) + | d2, h :: t -> + derivative_tr (d2 - 1, t) (d1 + 1, (float_of_int d2 *. h) :: p1)) + +(* Original version: + (* 2 *) + let horner x_value polynomial = + let result = ref(0.0) in + match polynomial with + |(a,b) -> List.iter (fun y -> result := !result *. x_value +. y) b; + !result + (* 3 *) + let derivative polynomial = + let resulting_polynomial = ref [] in + match polynomial with + |(a,b)->(let i = ref (List.length b - 1) in + List.iter (fun y -> (if !i <> 0 then (resulting_polynomial := !resulting_polynomial @ [(float_of_int !i) *. y]; i := !i - 1;))) b; + ((List.length !resulting_polynomial - 1),!resulting_polynomial)) + + (* 4 Potentially incorrect type (not polynomial -> polynomial -> polynomial)*) + let derivative_polynomial = ref [] + + let rec derivative_tr polynomial = + match polynomial with + |(a,b) -> if a = 0 then ((List.length !derivative_polynomial - 1),!derivative_polynomial) + else match b with + |h::t -> (derivative_polynomial := !derivative_polynomial @ [h *. float_of_int a]; + derivative_tr (a-1,t)) *) diff --git a/exercises/smelodesousa/F5/5-horner/template.ml b/exercises/smelodesousa/F5/5-horner/template.ml new file mode 100644 index 0000000..5174655 --- /dev/null +++ b/exercises/smelodesousa/F5/5-horner/template.ml @@ -0,0 +1,4 @@ +let answer = Unanswered +let horner x p = failwith "Unanswered" +let derivative p = failwith "Unanswered" +let rec derivative_tr p acc = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-horner/test.ml b/exercises/smelodesousa/F5/5-horner/test.ml new file mode 100644 index 0000000..144a63c --- /dev/null +++ b/exercises/smelodesousa/F5/5-horner/test.ml @@ -0,0 +1,82 @@ +open Test_lib +open Report +open List +open Random + +exception SeenLoops + +let polynomial_gen () = + let l = + sample_list ~min_size:3 ~max_size:10 + (fun () -> + let () = Random.self_init () in + Random.float 10.) + () + in + let n = List.length l - 1 in + (n, l) + +let polynomial_tr_gen () = (polynomial_gen (), (-1, [])) + +let hornerR = + Section + ( [ Text "Testing function"; Code "horner" ], + test_function_2_against_solution [%ty: float -> int * float list -> float] + "horner" + ~sampler:(fun () -> (Random.float 10., polynomial_gen ())) + ~gen:10 + [ + (3.0, (4, [ 3.; 0.; 5.; 0.; 1. ])); + (3.0, (-1, [ 1. ])); + (3.0, (0, [])); + (3.0, (-1, [])); + ] ) + +let derivativeA = + Section + ( [ Text "Testing function"; Code "derivative" ], + test_function_1_against_solution + [%ty: int * float list -> int * float list] "derivative" + ~sampler:polynomial_gen ~gen:10 + [ (4, [ 3.; 0.; 5.; 0.; 1. ]); (-1, [ 1. ]); (0, []); (-1, []) ] ) + +let failWith msg = [ Message ([ Text msg ], Failure) ] + +let checkForLoops cb = + find_binding code_ast "derivative_tr" @@ fun expr -> + let contains_loops = + Parsetree.( + function + | { pexp_desc = Pexp_for _ } | { pexp_desc = Pexp_while _ } -> + raise SeenLoops + | _ -> []) + in + try + ast_check_expr ~on_expression:contains_loops expr; + cb () + with SeenLoops -> + failWith + "Loops are not allowed on this exercise! Implement a recursive version." + +let derivative_trT = + Section + ( [ Text "Testing function"; Code "derivative_tr" ], + test_function_2_against_solution + [%ty: int * float list -> int * float list -> int * float list] + "derivative_tr" ~sampler:polynomial_tr_gen ~gen:10 + [ + ((4, [ 3.; 0.; 5.; 0.; 1. ]), (-1, [])); + ((-1, [ 1. ]), (-1, [])); + ((0, []), (-1, [])); + ((-1, []), (-1, [])); + ] ) + +let choiceT = + Section + ( [ Text "Testing variable answer" ], + test_variable_against_solution [%ty: choice] "answer" ) + +let () = + set_result @@ ast_sanity_check code_ast + @@ fun () -> + checkForLoops @@ fun () -> [ hornerR; derivativeA; derivative_trT; choiceT ] diff --git a/exercises/smelodesousa/F5/5-lists-sublists-1/descr.md b/exercises/smelodesousa/F5/5-lists-sublists-1/descr.md new file mode 100644 index 0000000..f70f0b0 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-1/descr.md @@ -0,0 +1,9 @@ +We pretend to define the function `sublist: 'a list -> 'a list list` which returns the list containing all sublists of a list `l`, with the elements presented in the order of the original list `l`. + +For example, `sublist ['a'; 'b'; 'c']` = `[[];['c'];['b'];['b'; 'c'];['a'];['a'; 'c'];['a'; 'b'];['a'; 'b'; 'c']]`. Note that `['a'; 'c']` is a sublist of `['a'; 'b'; 'c']`, but `['c'; 'a']` is not. + +1. What is the expected result of `sublist []`? +2. What is the expected result of `sublist ['c']`? +3. What is the expected result of `sublist ['b'; 'c']`? +4. What recursive pattern can we infer from these previous examples for the `sublist` function?
+Considering that pattern, define the function `sublist : char list -> char list list`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-1/meta.json b/exercises/smelodesousa/F5/5-lists-sublists-1/meta.json new file mode 100644 index 0000000..5f8cbbe --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-1/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Lists and sublists I", + "identifier" : "5.9", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], + "backward_exercises": ["smelodesousa/F5/5-seq-true"] +} diff --git a/exercises/smelodesousa/F5/5-lists-sublists-1/prelude.ml b/exercises/smelodesousa/F5/5-lists-sublists-1/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-lists-sublists-1/prepare.ml b/exercises/smelodesousa/F5/5-lists-sublists-1/prepare.ml new file mode 100644 index 0000000..acdb6e6 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-1/prepare.ml @@ -0,0 +1 @@ +let replace_with_your_solution = [["replace with your solution"]] \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-1/solution.ml b/exercises/smelodesousa/F5/5-lists-sublists-1/solution.ml new file mode 100644 index 0000000..f9e378b --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-1/solution.ml @@ -0,0 +1,10 @@ +let answer1 = [[]] + +let answer2 = [[]; ['c']] + +let answer3 = [[]; ['c']; ['b']; ['b'; 'c']] + +let rec sublist l = + match l with + [] -> [[]] + | h::t -> let sl = (sublist t) in sl @ (List.map (fun l -> h::l) sl) \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-1/template.ml b/exercises/smelodesousa/F5/5-lists-sublists-1/template.ml new file mode 100644 index 0000000..ebd54d0 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-1/template.ml @@ -0,0 +1,7 @@ +let answer1 = replace_with_your_solution + +let answer2 = replace_with_your_solution + +let answer3 = replace_with_your_solution + +let rec sublist l = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-1/test.ml b/exercises/smelodesousa/F5/5-lists-sublists-1/test.ml new file mode 100644 index 0000000..2dd568f --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-1/test.ml @@ -0,0 +1,41 @@ +open Test_lib +open Report + +let testAnswer1 = + set_progress "Correcting exercise 1" ; + Section ([ Text "Exercise 1: " ; Code "" ], + test_variable_against_solution + [%ty: 'a list list] + "answer1") + +let testAnswer2 = + set_progress "Correcting exercise 2" ; + Section ([ Text "Exercise 2: " ; Code "" ], + test_variable_against_solution + [%ty: char list list] + "answer2") + +let testAnswer3 = + set_progress "Correcting exercise 3" ; + Section ([ Text "Exercise 3: " ; Code "" ], + test_variable_against_solution + [%ty: char list list] + "answer3") + + +let testSublist = + set_progress "Correcting exercise 4" ; + Section ( + [ Text "Exercise 4: " ; Code "" ], + test_function_1_against_solution + [%ty: char list -> char list list] + "sublist" + ~sampler: (sample_list ~min_size: 3 ~max_size: 10 sample_char) + ~gen: 7 + [] + ) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ testAnswer1 ; testAnswer2 ; testAnswer3 ; testSublist ] diff --git a/exercises/smelodesousa/F5/5-lists-sublists-2/descr.md b/exercises/smelodesousa/F5/5-lists-sublists-2/descr.md new file mode 100644 index 0000000..f6d2c54 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-2/descr.md @@ -0,0 +1,14 @@ +We pretend to define the function `insertion: 'a -> 'a list -> 'a list list` which returns all the possible ways to insert an element `e` in a list `l`. Therefore, inserting `'e'` in the list `['a'; 'b'; 'c']` may result in the following lists: + +- `['e'; 'a'; 'b'; 'c']`; +- `['a'; 'e'; 'b'; 'c']`; +- `['a'; 'b'; 'e'; 'c']`; +- `['a'; 'b'; 'c'; 'e']`. + +Answer the following questions: + +1. What is the expected result of `insertion 'e' []`? +2. What is the expected result of `insertion 'e' [c]`? +3. What is the expected result of `insertion 'e' [b;c]`? +4. What recursive pattern can we infer from these previous examples for the `insertion` function?
+Considering that pattern, define the function `insertion : char -> char list -> char list list`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-2/meta.json b/exercises/smelodesousa/F5/5-lists-sublists-2/meta.json new file mode 100644 index 0000000..0628a48 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-2/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Lists and sublists II", + "identifier" : "5.10", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], + "backward_exercises": ["smelodesousa/F5/5-lists-sublists-1"] +} diff --git a/exercises/smelodesousa/F5/5-lists-sublists-2/prelude.ml b/exercises/smelodesousa/F5/5-lists-sublists-2/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-lists-sublists-2/prepare.ml b/exercises/smelodesousa/F5/5-lists-sublists-2/prepare.ml new file mode 100644 index 0000000..acdb6e6 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-2/prepare.ml @@ -0,0 +1 @@ +let replace_with_your_solution = [["replace with your solution"]] \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-2/solution.ml b/exercises/smelodesousa/F5/5-lists-sublists-2/solution.ml new file mode 100644 index 0000000..74ead0b --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-2/solution.ml @@ -0,0 +1,9 @@ +let answer1 = [['e']] + +let answer2 = [['e'; 'c']; ['c'; 'e']] + +let answer3 = [['e'; 'b'; 'c']; ['b'; 'e'; 'c']; ['b'; 'c'; 'e']] + +let rec insertion e l = match l with + [] -> [[e]] + | x::xs -> (e::l) :: (List.map (fun li -> x::li) (insertion e xs)) diff --git a/exercises/smelodesousa/F5/5-lists-sublists-2/template.ml b/exercises/smelodesousa/F5/5-lists-sublists-2/template.ml new file mode 100644 index 0000000..c63339d --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-2/template.ml @@ -0,0 +1,8 @@ +let answer1 = replace_with_your_solution + +let answer2 = replace_with_your_solution + +let answer3 = replace_with_your_solution + +let rec insertion e l = + failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-2/test.ml b/exercises/smelodesousa/F5/5-lists-sublists-2/test.ml new file mode 100644 index 0000000..08255bd --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-2/test.ml @@ -0,0 +1,41 @@ +open Test_lib +open Report + +let testAnswer1 = + set_progress "Correcting exercise 1" ; + Section ([ Text "Exercise 1: " ; Code "" ], + test_variable_against_solution + [%ty: char list list] + "answer1") + +let testAnswer2 = + set_progress "Correcting exercise 2" ; + Section ([ Text "Exercise 2: " ; Code "" ], + test_variable_against_solution + [%ty: char list list] + "answer2") + +let testAnswer3 = + set_progress "Correcting exercise 3" ; + Section ([ Text "Exercise 3: " ; Code "" ], + test_variable_against_solution + [%ty: char list list] + "answer3") + + +let testInsertions = + set_progress "Correcting exercise 4" ; + Section ( + [ Text "Exercise 4: " ; Code "" ], + test_function_2_against_solution + [%ty: char -> char list -> char list list] + "insertion" + ~sampler: (fun () -> (sample_char(), (sample_list ~min_size: 3 ~max_size: 9 sample_char)())) + ~gen: 7 + [] + ) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ testAnswer1 ; testAnswer2 ; testAnswer3 ; testInsertions ] diff --git a/exercises/smelodesousa/F5/5-lists-sublists-3/descr.md b/exercises/smelodesousa/F5/5-lists-sublists-3/descr.md new file mode 100644 index 0000000..cacb41b --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-3/descr.md @@ -0,0 +1,7 @@ +Let's now define the function `permutation` which returns the list containing all permutations of the parameter list `l`. For example, `permutation ['a'; 'b'; 'c'] = [['a'; 'b'; 'c']; ['b'; 'a'; 'c']; ['b'; 'c'; 'a']; ['a'; 'c'; 'b']; ['c'; 'a'; 'b']; ['c'; 'b'; 'a']]`. + +1. What is the expected result of `permutation []`? +2. What is the expected result of `permutation ['c']`? +3. What is the expected result of `permutation ['b'; 'c']`? +4. What recursive pattern can we infer from these previous examples for the `permutation` function?
+Considering that pattern, define the function `permutation : char list -> char list list`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-3/meta.json b/exercises/smelodesousa/F5/5-lists-sublists-3/meta.json new file mode 100644 index 0000000..40c209c --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-3/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Lists and sub-lists III", + "identifier" : "5.11", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], + "backward_exercises": ["smelodesousa/F5/5-lists-sublists-2"] +} diff --git a/exercises/smelodesousa/F5/5-lists-sublists-3/prelude.ml b/exercises/smelodesousa/F5/5-lists-sublists-3/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-lists-sublists-3/prepare.ml b/exercises/smelodesousa/F5/5-lists-sublists-3/prepare.ml new file mode 100644 index 0000000..acdb6e6 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-3/prepare.ml @@ -0,0 +1 @@ +let replace_with_your_solution = [["replace with your solution"]] \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-3/solution.ml b/exercises/smelodesousa/F5/5-lists-sublists-3/solution.ml new file mode 100644 index 0000000..65bd7c2 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-3/solution.ml @@ -0,0 +1,15 @@ +open List + +let answer1 = [[]] + +let answer2 = [['c']] + +let answer3 = [['b'; 'c']; ['c'; 'b']] + +let rec insertion e l = match l with + [] -> [[e]] + | x::xs -> (e::l) :: (map (fun li -> x::li) (insertion e xs)) + +let rec permutation l = match l with + [] -> [[]] + | x::xs -> flatten (map (fun l -> (insertion x l)) (permutation xs)) diff --git a/exercises/smelodesousa/F5/5-lists-sublists-3/template.ml b/exercises/smelodesousa/F5/5-lists-sublists-3/template.ml new file mode 100644 index 0000000..d997273 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-3/template.ml @@ -0,0 +1,8 @@ +let answer1 = replace_with_your_solution + +let answer2 = replace_with_your_solution + +let answer3 = replace_with_your_solution + +let permutation e l = + failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-3/test.ml b/exercises/smelodesousa/F5/5-lists-sublists-3/test.ml new file mode 100644 index 0000000..e515454 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-3/test.ml @@ -0,0 +1,41 @@ +open Test_lib +open Report + +let testAnswer1 = + set_progress "Correcting exercise 1" ; + Section ([ Text "Exercise 1: " ; Code "" ], + test_variable_against_solution + [%ty: char list list] + "answer1") + +let testAnswer2 = + set_progress "Correcting exercise 2" ; + Section ([ Text "Exercise 2: " ; Code "" ], + test_variable_against_solution + [%ty: char list list] + "answer2") + +let testAnswer3 = + set_progress "Correcting exercise 3" ; + Section ([ Text "Exercise 3: " ; Code "" ], + test_variable_against_solution + [%ty: char list list] + "answer3") + + +let testPermutations = + set_progress "Correcting exercise 4" ; + Section ( + [ Text "Exercise 4: " ; Code "" ], + test_function_1_against_solution + [%ty: char list -> char list list] + "permutation" + ~sampler: (sample_list ~min_size: 2 ~max_size: 4 sample_char) + ~gen: 7 + [] + ) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ testAnswer1 ; testAnswer2 ; testAnswer3 ; testPermutations ] diff --git a/exercises/smelodesousa/F5/5-lists-sublists-4/descr.md b/exercises/smelodesousa/F5/5-lists-sublists-4/descr.md new file mode 100644 index 0000000..b01775d --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-4/descr.md @@ -0,0 +1,22 @@ + + + + + +Finally, let's define `subbag l`, which calculates the list of all of the permutations of every sublist of `l`. For example:
+`subbag ['a'; 'b'; 'c'] = [[]; ['a'] ; ['b']; ['c'] ; ['a'; 'b'] ; ['a'; 'c'] ; ['b'; 'a'] ; ['b'; 'c'] ; ['c'; 'a'] ; ['c'; 'b'] ; ['a'; 'b'; 'c']; ['a'; 'c'; 'b'] ; ['b'; 'a'; 'c'] ; ['b'; 'c'; 'a']; ['c'; 'b'; 'a'] ; ['c'; 'a'; 'b']]`. + +This function calculates something more "explosive" than the group of all subsets of a certain set (or list), given that the order is relevant
+(`['a'; 'b']` $ne$ `['b'; 'a']`). + +1. Define the function `subbag : char list -> char list list`, with the functions defined in the previous exercises in mind. + +Isn't there a way of defining the function without using the previous ones? To achieve that, use the incremental methodology previously recommended to extract a programmable pattern. We propose this challenge for your curiosity. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-4/meta.json b/exercises/smelodesousa/F5/5-lists-sublists-4/meta.json new file mode 100644 index 0000000..b4d3a82 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-4/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Lists and sub-lists IV", + "identifier" : "5.12", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-lists-sublists-3"] +} diff --git a/exercises/smelodesousa/F5/5-lists-sublists-4/prelude.ml b/exercises/smelodesousa/F5/5-lists-sublists-4/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-lists-sublists-4/prepare.ml b/exercises/smelodesousa/F5/5-lists-sublists-4/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-lists-sublists-4/solution.ml b/exercises/smelodesousa/F5/5-lists-sublists-4/solution.ml new file mode 100644 index 0000000..31b04f4 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-4/solution.ml @@ -0,0 +1,18 @@ +let rec sublists = function + | [] -> [[]] + | x :: xs -> + let subl = (sublists xs) in + subl @ (List.map (fun l -> x::l) subl) + +let rec insertions e l = match l with + | [] -> [[e]] + | x :: xs -> (e :: l) :: (List.map (fun li -> x :: li) (insertions e xs)) + +let rec permutations l = match l with + | [] -> [[]] + | x::xs -> List.concat (List.map (fun l -> (insertions x l)) (permutations xs)) + +let subbag l = + List.sort compare + (List.concat + (List.map (permutations) (sublists l)));; diff --git a/exercises/smelodesousa/F5/5-lists-sublists-4/template.ml b/exercises/smelodesousa/F5/5-lists-sublists-4/template.ml new file mode 100644 index 0000000..60caf49 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-4/template.ml @@ -0,0 +1,2 @@ +let subbag l = + failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists-sublists-4/test.ml b/exercises/smelodesousa/F5/5-lists-sublists-4/test.ml new file mode 100644 index 0000000..634c3d5 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists-sublists-4/test.ml @@ -0,0 +1,15 @@ +open Test_lib +open Report + +let testSubbag () = + test_function_1_against_solution + [%ty: char list -> char list list] + "subbag" + ~sampler: (sample_list ~min_size: 2 ~max_size: 4 sample_char) + ~gen: 10 + [] + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + testSubbag () diff --git a/exercises/smelodesousa/F5/5-lists/descr.md b/exercises/smelodesousa/F5/5-lists/descr.md new file mode 100644 index 0000000..05705cd --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists/descr.md @@ -0,0 +1,23 @@ +Define the following functions about lists: + +1. The function `sum : int list -> int` which returns the sum of the integers contained in the received list. +For example, `sum [1;5;3]` returns `9`.
+ +2. The function `count_even : int list -> int` which returns the amount of even numbers present in the received list of integers. For example, `count_even [1;-6;3;17;4;80;-18]` returns `4`. + +3. The boolean function `palindrome : int list -> bool `, which returns `true` if the list in parameter is a palindrome, or `false` otherwise. For example, `palindrome [1;5;3;5;1]` returns `true`. + +4. The function `uppercase : char list -> char list` which transforms each character in the list that is a lowercase letter to an uppercase letter. For example, `uppercase ['a';'9';'T';'%';'z';'-']` returns `['A';'9';'T';%';'Z';'-']`. + +5. The boolean function `is_sorted : int list -> (int -> int -> int) -> bool`, which returns true if the list in parameter is sorted according to sorting criteria indicated by the second parameter.
+Therefore, `is_sorted [1;3;7;9] compare` returns `true`, and `is_sorted [1;3;7;9] (fun a b -> compare b a)` returns `false`. Remember that the function `compare` from the OCaml standard library is defined as follows:
+```ocaml +compare a b = -1 if a < b +compare a b = 1 if a > b +compare a b = 0 if a = b +``` + +6. The function `remove_duplicate_sorted : int list -> int list` which removes duplicated elements from a list that is assumed to be sorted. For example, `remove_duplicate_sorted [1;1;2;2;3,5;5;6;7;8;8;8;9]` = `[1;2;3;5,6;7;8;9]`. + +7. The function `remove_ duplicate : int list -> int list` which removes duplicated elements from a list. In this case, it is not assumed that the list is sorted. `remove_duplicate [9;1;7;6;6;7;8;1;2;6;2;3;5;5;1;9]` = `[1;2;3;5,6;7;8;9]`.
+Try, as much as possible, to use list operators from the OCaml module `List` (fold_left, map, for_all, iter, exists, filter, etc.). \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists/meta.json b/exercises/smelodesousa/F5/5-lists/meta.json new file mode 100644 index 0000000..3b6ef69 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Utilities about Lists", + "identifier" : "5.1", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], + "backward_exercises": ["mooc/week3/seq1/ex1"] +} diff --git a/exercises/smelodesousa/F5/5-lists/prelude.ml b/exercises/smelodesousa/F5/5-lists/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-lists/prepare.ml b/exercises/smelodesousa/F5/5-lists/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-lists/solution.ml b/exercises/smelodesousa/F5/5-lists/solution.ml new file mode 100644 index 0000000..da22072 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists/solution.ml @@ -0,0 +1,29 @@ +let sum lista = + List.fold_left (+) 0 lista + +let rec count_even l = + match l with + | [] -> 0 + | h::t -> if (h mod 2) = 0 then 1 + (count_even t) else (count_even t) + +let palindrome l = + l = List.rev l + +let uppercase l = + List.map (fun x -> if (x >= 'a' && x <= 'z') then char_of_int((int_of_char x) - 32) else x) l + +let rec is_sorted (l : int list) f = + match l with + | [] -> true + | h1::[] -> true + | h1::h2::t -> if ((f h1 h2) = (-1) || (f h1 h2) = 0) then (true && is_sorted (h2::t) f) else false + +let rec remove_duplicate_sorted l = + match l with + | [] -> [] + | h::t -> h::(remove_duplicate_sorted (List.filter (fun x -> x<>h) t)) + +let rec remove_duplicate l = + match (List.sort compare l) with + | [] -> [] + | h::t -> h::(remove_duplicate (List.filter (fun x -> x<>h) t)) \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lists/template.ml b/exercises/smelodesousa/F5/5-lists/template.ml new file mode 100644 index 0000000..c95a17c --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists/template.ml @@ -0,0 +1,20 @@ +let sum l = + failwith "Replace with your solution" + +let rec count_even l = + failwith "Replace with your solution" + +let palindrome l = + failwith "Replace with your solution" + +let uppercase l = + failwith "Replace with your solution" + +let rec is_sorted l f = + failwith "Replace with your solution" + +let rec remove_duplicate_sorted l = + failwith "Replace with your solution" + +let rec remove_duplicate l = + failwith "Replace with your solution" diff --git a/exercises/smelodesousa/F5/5-lists/test.ml b/exercises/smelodesousa/F5/5-lists/test.ml new file mode 100644 index 0000000..a392c2b --- /dev/null +++ b/exercises/smelodesousa/F5/5-lists/test.ml @@ -0,0 +1,136 @@ +open Test_lib +open Report + +(* samplers *) +let sample_Palindrome () = + let () = Random.self_init () in + if ((Random.int 2) = 0) + then + let l = (sample_list ~min_size: 5 ~max_size: 20 (fun () -> ((Random.int 1000) - 500)) ()) in + if ((Random.int 2) = 0) + then + l@(List.rev l) + else + ((l@[((Random.int 1000) - 500)])@(List.rev l)) + else + sample_list ~min_size: 5 ~max_size: 20 (fun () -> ((Random.int 1000) - 500)) () + +let sample_allChar () = + let () = Random.self_init () in + char_of_int ((Random.int 95) + 32) + +let sample_Slist () = + let () = Random.self_init () in + if ((Random.int 2) = 1) + then let l = (sample_list ~min_size: 5 ~max_size: 20 (fun () -> ((Random.int 100) - 50))) in ((l()) , compare) + else let l = (sample_list ~min_size: 5 ~max_size: 20 ~sorted: true (fun () -> ((Random.int 100) - 50))) in ((l()) , compare) + +let sample_sorted_dupped_list () = + let s = ref (-1) in + let f = ref (-1) in + let () = Random.self_init () in + let rec ranged_list f l = + if f > l + then [] + else + match Random.int 4 with + | 0 -> ranged_list (f+1) l + | 1 -> f::(ranged_list (f+1) l) + | 2 -> f::f::(ranged_list (f+1) l) + | 3 -> f::f::f::(ranged_list (f+1) l) in + begin + if ((Random.int 2) = 0) + then + s := -(Random.int 15) + else + s := (Random.int 50); + f := (Random.int 16) + 15; + ranged_list !s !f; + end + +let sample_dupped_list () = + let () = Random.self_init () in + let size = Random.int(15) + 1 in + let rec ranged_list pos size = + if pos > size + then [] + else + let () = Random.self_init () in + ((Random.int 9) + 1)::(ranged_list (pos+1) size) in + ranged_list 0 size + +(* correctors *) +let sumS = + set_progress "Correcting question 1" ; + Section ([ Text "Exercise 1: " ; Code "sum" ], + test_function_1_against_solution + [%ty: int list -> int] + "sum" + ~sampler: (sample_list ~min_size: 5 ~max_size: 20 (fun () -> let () = Random.self_init () in (Random.int 500))) + ~gen: 8 + [([]); [1]]) + +let countEvenS = + set_progress "Correcting question 2" ; + Section ([ Text "Exercise 2: " ; Code "count_even" ], + test_function_1_against_solution + [%ty: int list -> int] + "count_even" + ~sampler: (sample_list ~min_size: 5 ~max_size: 20 (fun () -> let () = Random.self_init () in ((Random.int 1000) - 500))) + ~gen: 7 + [([]); [1]; [2]]) + +let palindromeS = + set_progress "Correcting question 3" ; + Section ([ Text "Exercise 3: " ; Code "palindrome" ], + test_function_1_against_solution + [%ty: int list -> bool] + "palindrome" + ~sampler: sample_Palindrome + ~gen: 6 + [([]); [1]; [2; 2]; [2; 1; 2]]) + +let uppercaseS = + set_progress "Correcting question 4" ; + Section ([ Text "Exercise 4: " ; Code "uppercase" ], + test_function_1_against_solution + [%ty: char list -> char list] + "uppercase" + ~sampler: (sample_list ~min_size: 5 ~max_size: 20 sample_allChar) + ~gen: 7 + [([]); ['a']; ['%']]) + +let is_sortedS = + set_progress "Correcting question 5" ; + Section ([ Text "Exercise 5: " ; Code "is_sorted" ], + test_function_2_against_solution + [%ty: int list -> (int -> int -> int) -> bool] + "is_sorted" + ~sampler: (sample_Slist) + ~gen: 8 + [([], compare); ([-1;0;0;1], compare)]) + +let remove_sorted_dupsS = + set_progress "Correcting question 6" ; + Section ([ Text "Exercise 6: " ; Code "remove_duplicate_sorted" ], + test_function_1_against_solution + [%ty: int list -> int list] + "remove_duplicate_sorted" + ~sampler: sample_sorted_dupped_list + ~gen: 8 + [([]); ([-1;0;0;1])]) + +let remove_dupsS = + set_progress "Correcting question 7" ; + Section ([ Text "Exercise 7: " ; Code "remove_duplicate" ], + test_function_1_against_solution + [%ty: int list -> int list] + "remove_duplicate" + ~sampler: sample_dupped_list + ~gen: 8 + [([]); ([-1;0;0;1])]) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ sumS; countEvenS; palindromeS; uppercaseS; is_sortedS; remove_sorted_dupsS; remove_dupsS ] diff --git a/exercises/smelodesousa/F5/5-lotto/descr.md b/exercises/smelodesousa/F5/5-lotto/descr.md new file mode 100644 index 0000000..f12bff8 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lotto/descr.md @@ -0,0 +1,27 @@ + + + + + +In this exercise, we'll simulate a lotto prize draw. + +1. Define the `make_grid` function that, given an integer `n`, returns a grid (matrix) of size $n\times n$ initialized with `false` values. + +2. Specify the type of the grid that the function defined above creates. + +3. Define the `grid` global variable using the function above and with size $7 \times 7$. + +4. Define the `fill: int list -> grids` function that, given a list with $7$ distinct integers comprised between $1$ and $49$, creates and fills a grid that is returned in the end. On the created grid, a position from the prize draw (from the list parameter) has a `true` value. It is important to note that the grid positions correspond to the number of the prize draw.
+For example, the 7th position on the grid, grids.(0).(6), is the number $7$ of the prize draw. + +5. Define the `prize_draw : grids -> int list -> int -> (int list * bool)` that given a draw (list of $6$ integers, plus another integer -- the complementary) returns the correct guesses. + +For example if the draw is $1$, $5$, $23$, $30$, $31$ and $45$ and the complementary number is $17$, and if the `grid` with the complementary is $1$, $17$ and $30$, then the answer to `prize_draw grid [1; 5; 23; 30; 31; 45] 17` should be `([1; 30], true)`, meaning "you got $1$ and $30$ right, and were also right on the complementary". \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lotto/meta.json b/exercises/smelodesousa/F5/5-lotto/meta.json new file mode 100644 index 0000000..692ff60 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lotto/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Lotto", + "identifier" : "5.3", + "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-shine-in-society"] +} diff --git a/exercises/smelodesousa/F5/5-lotto/prelude.ml b/exercises/smelodesousa/F5/5-lotto/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-lotto/prepare.ml b/exercises/smelodesousa/F5/5-lotto/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-lotto/solution.ml b/exercises/smelodesousa/F5/5-lotto/solution.ml new file mode 100644 index 0000000..a5e4b74 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lotto/solution.ml @@ -0,0 +1,29 @@ +(* 1 *) +let make_grid n = Array.make_matrix n n false + +(* 2 *) +type grids = bool array array + +(* 3 *) +let grid = make_grid 7 + +(* 4 *) +let fill l : grids = + let grid = make_grid 7 in + let rec go_through_list l = + match l with + | [] -> () + | h :: t -> let i = (h - 1) / 7 in grid.(i).((h - 1) - (i * 7)) <- true; go_through_list t in + go_through_list l; + grid + +(* 5 *) +let prize_draw (g : grids) l c = + let get_value n = + let i = (n - 1) / 7 in + g.(i).((n - 1) - (i * 7)) in + let rec get_output g l = + match l with + | [] -> [] + | h :: t -> if get_value h then h :: (get_output g t) else get_output g t in + (get_output g l, get_value c) \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-lotto/template.ml b/exercises/smelodesousa/F5/5-lotto/template.ml new file mode 100644 index 0000000..34bd482 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lotto/template.ml @@ -0,0 +1,9 @@ +let make_grid n = failwith "Replace with your solution" + +type grids = Replace_with_your_solution + +let grid = failwith "Replace with your solution" + +let fill l = failwith "Replace with your solution" + +let prize_draw g l c = failwith "Replace with your solution" diff --git a/exercises/smelodesousa/F5/5-lotto/test.ml b/exercises/smelodesousa/F5/5-lotto/test.ml new file mode 100644 index 0000000..c231fd1 --- /dev/null +++ b/exercises/smelodesousa/F5/5-lotto/test.ml @@ -0,0 +1,123 @@ +open Test_lib +open Report + +exception FoundUnique of int + +let rec remove matches = function + | [] -> [] + | x :: xs when x = matches -> remove matches xs + | x :: xs -> x :: remove matches xs + +let init n f = + let l = ref [] in + for i = 0 to n-1 do + l := (f i)::!l + done; + !l + +(* n: maximum size of generated list *) +let generate_list () = + let possible_values, possible_values_sz = (ref (init 49 (fun x -> x+1))), ref 49 in + let sz = (Random.int 25) + 5 in + let l = ref [] in + for i = 0 to (sz-1) do + (* there cant be any duplicates in the list *) + let v = List.nth !possible_values (Random.int !possible_values_sz) in + possible_values := remove v !possible_values; + possible_values_sz := !possible_values_sz - 1; + l := v::(!l) + done; + !l + +let generate_grid l = + let g = Array.make_matrix 7 7 false in + + List.iter (fun e -> + let e = if (Random.int 100) >= 80 then (Random.int 49) else e-1 in + let i = (e/7) in + let j = abs(e - (i*7)) in + begin + match (Random.int 100) with + | _ as x when x >= 0 && x <= 80 -> g.(i).(j) <- false + | _ -> g.(i).(j) <- true + end; + )l; + g + +let draw_sampler () = + let l = generate_list () in + let g = generate_grid l in + let complementary = + (try while true do + let v = (Random.int 49) + 1 in + if not (List.mem v l) then raise (FoundUnique v) + done; + -1 + with FoundUnique v -> v) in + g, l, complementary + +let correct_answer id name = + Section ([ Text id ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Correct answer" ], Success 5)]) + +let wrong_answer id name = + Section ([ Text id ; Code "solution" ], + [Message ([ Text "Checking that " ; Code name ; Text "is correct "], Informative) ; + Message ([ Text "Wrong answer" ], Failure)]) + +let compatible_type ~expected:exp got = + match Introspection.compatible_type exp ("Code." ^ got) with + | Introspection.Absent -> false + | Introspection.Incompatible _ -> false + | Introspection.Present () -> true + +let q1 = + Section([Text "Testing make_grid function"], + test_function_1_against_solution + [%ty: int -> bool array array] + "make_grid" + ~gen:8 + ~sampler: (fun () -> Random.int 20) + [0; 10] + ) + +type correct_p1 = bool array array +let q2 = + let r2 = compatible_type "correct_p1" "grids" in + match r2 with + | true -> correct_answer "Exercise 2: " "grids" + | _ -> wrong_answer "Exercise 2: " "grids" + +let q3 = + let grid = Array.make_matrix 7 7 false in + Section([Text "Testing global variable grid"], + test_variable_against_solution + [%ty: bool array array] + "grid" + ) + +let q4 = + Section([Text "Testing fill function"], + test_function_1_against_solution + [%ty: int list -> bool array array] + "fill" + ~gen:17 + ~sampler: generate_list + [[]; [1; 49]; [5; 6; 25; 10]] + ) + +let q5 = + Section([Text "Testing prize_draw function"], + test_function_3_against_solution + [%ty: bool array array -> int list -> int -> (int list * bool)] + "prize_draw" + ~gen:40 + ~sampler:draw_sampler + [] + ) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [q1; q2; q3; q4; q5] diff --git a/exercises/smelodesousa/F5/5-max-sub-list/descr.md b/exercises/smelodesousa/F5/5-max-sub-list/descr.md new file mode 100644 index 0000000..9881b16 --- /dev/null +++ b/exercises/smelodesousa/F5/5-max-sub-list/descr.md @@ -0,0 +1,32 @@ + + + + + +# Introduction + +The maximum sublist problem consists of, given a list of integers, finding the contiguous sublist with the largest sum of all existing contiguous sublists. + +For example, looking at the list `[ -3; 6; -3; 4; -1; 2; 2; -5; 4 ]`, the sublist with the largest sum is `[6; -3; 4; -1; 2; 2]`, which sum is 10. This sublist might not be unique, however. The list `[ -3; 7; -11; 4; -1; 2; 2; -5; 4 ]` has two sublists with a sum of 7. The maximum sublist might not be unique, but the largest sum is. + +To efficiently solve this problem, in 1984, Jay Kadane (from Carnegie Mellon University) presented an algorithm that solves this problem by going through the list just once. + +The algorithm is defined recursively in the following manner: +- If the list l is empty, then the sum is $0$. +- If the list l is $[v_1; v_2; \ldots ; v_ {i−1}; v_i; \ldots ; v_n]$ and $m$ is the largest possible sum for the sublist that ends in $i-1$, then the biggest sum of $[v_1; v_2; \ldots ; v_ {i−1}; v_i]$ is $max(v_i, m+v_i)$. + + Note: The empty list will be considered the leftmost sublist of any list. Its sum is also 0. + +# Objectives + +1. Implement a function `max_kadane : int list -> int` that incorporates the previously shown Kadene's algorithm and returns the largest possible sum of a contiguous sublist from the original list. If necessary, you may use an extra list to memoize the previously found largest sums. + +2. Implement a function `kadane : int list -> int list` that returns the sublist which has the largest sum. In case there are multiple sublists, you may return the leftmost one. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-max-sub-list/meta.json b/exercises/smelodesousa/F5/5-max-sub-list/meta.json new file mode 100644 index 0000000..fe28162 --- /dev/null +++ b/exercises/smelodesousa/F5/5-max-sub-list/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "The maximum sublist problem", + "identifier": "5.15", + "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-gray-codes"] +} diff --git a/exercises/smelodesousa/F5/5-max-sub-list/prelude.ml b/exercises/smelodesousa/F5/5-max-sub-list/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-max-sub-list/prepare.ml b/exercises/smelodesousa/F5/5-max-sub-list/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-max-sub-list/solution.ml b/exercises/smelodesousa/F5/5-max-sub-list/solution.ml new file mode 100644 index 0000000..3f0342a --- /dev/null +++ b/exercises/smelodesousa/F5/5-max-sub-list/solution.ml @@ -0,0 +1,66 @@ +(* 1 *) +let max_kadane l = + let rec aux sum ret = function + | h :: t -> + let cur_max = max (sum + h) h in + aux cur_max (max cur_max ret) t + | [] -> ret + in + match l with + | [] -> 0 + | h :: t -> + let result = aux h h t in + if result < 0 then 0 else result + +(* 2 *) +let kadane l = + let rec aux sum maxVal ret l = + if sum = maxVal then ret + else + match l with + | h :: t -> + if sum + h < h then aux h maxVal [ h ] t + else aux (sum + h) maxVal (h :: ret) t + | [] -> ret + in + match l with + | h :: t -> + let max = max_kadane l in + if max = 0 then [] else List.rev (aux h max [ h ] t) + | [] -> [] + +(* Original version: + (* 1 *) + let rec max_liste l = + match l with + | [] -> assert false + | [x] -> x + | x :: s -> max x (max_liste s) + + let max_kadane l = + let rec kad_rec l m = + match l, m with + | [], _ -> max_liste m + | x :: r, y :: _ -> + let z = max x (x+y) in + kad_rec r (z::m) + | _ -> assert false + in + kad_rec l [0] + + (* 2 *) + let rec loop sum l seq maxsum maxseq = + match l with + | [] -> List.rev maxseq + | x::xs -> + let sum = sum + x and seq = x :: seq in + if sum < 0 then + loop 0 xs [] maxsum maxseq + else if sum > maxsum then + loop sum xs seq sum seq + else + loop sum xs seq maxsum maxseq + + + let kadane l = + loop 0 l [] 0 [] *) diff --git a/exercises/smelodesousa/F5/5-max-sub-list/template.ml b/exercises/smelodesousa/F5/5-max-sub-list/template.ml new file mode 100644 index 0000000..bfa76a2 --- /dev/null +++ b/exercises/smelodesousa/F5/5-max-sub-list/template.ml @@ -0,0 +1,2 @@ +let max_kadane l = failwith "Unanswered" +let kadane l = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-max-sub-list/test.ml b/exercises/smelodesousa/F5/5-max-sub-list/test.ml new file mode 100644 index 0000000..3b64746 --- /dev/null +++ b/exercises/smelodesousa/F5/5-max-sub-list/test.ml @@ -0,0 +1,39 @@ +open Test_lib +open Report +open List +open Random + +let max_kadaneQ = + Section + ( [ Text "Testing function"; Code "max_kadane" ], + test_function_1_against_solution [%ty: int list -> int] "max_kadane" + ~sampler: + (sample_list ~min_size:5 ~max_size:20 ~dups:true (fun () -> + Random.int 10 - 5)) + ~gen:10 + [ + [ -3; 6; -3; 4; -1; 2; 2; -5; 4 ]; + [ -3; 7; -11; 4; -1; 2; 2; -5; 4 ]; + [ -1; 0 ]; + [ -2; -1; -3 ]; + ] ) + +let kadaneQ = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "kadane" ], + test_function_1_against_solution [%ty: int list -> int list] "kadane" + ~sampler: + (sample_list ~min_size:5 ~max_size:20 ~dups:true (fun () -> + let () = Random.self_init () in + Random.int 10 - 5)) + ~gen:10 + [ + [ -3; 6; -3; 4; -1; 2; 2; -5; 4 ]; + [ -3; 7; -11; 4; -1; 2; 2; -5; 4 ]; + [ -1; 0 ]; + [ -2; -1; -3 ]; + ] ) + +let () = + set_result @@ ast_sanity_check code_ast @@ fun () -> [ max_kadaneQ; kadaneQ ] diff --git a/exercises/smelodesousa/F5/5-pizzaria/descr.md b/exercises/smelodesousa/F5/5-pizzaria/descr.md new file mode 100644 index 0000000..4ddb53a --- /dev/null +++ b/exercises/smelodesousa/F5/5-pizzaria/descr.md @@ -0,0 +1,53 @@ + + + + + +# Introduction + +Luigi’s new pizzeria has been the talk of the town in the past few weeks. Not only because it has the best pizzas you can find for miles, but also because of its crazy *all you can eat* policy. + +You see, Luigi’s pizzas are enormous, and they are cut into very thin slices. And that’s not even the craziest part! Each slice has different ingredients and you can eat as many slices as you want. But there is one small caveat. You can only select adjacent slices and you have to eat them all! It is therefore very tricky for each client to select the best part of the pizza according to his taste. + +> ------ +> +> ![](https://i.imgur.com/4BJ0iBk.png) +> +> Figure 1: Selected slices must be adjacent. The section in grey has a score of 20. +> +> ------ + +You enter the restaurant and see that today’s special pizza has been cut into *N* slices. After attributing scores $(S_1, \ldots , S_N)$ to each one of the slices, you must devise an algorithm that selects the section of the pizza that yields the best value according to those scores. The value of a section of pizza is the sum of the scores of its slices. Notice that slice scores can be negative. + +# Objectives + +Define a function `score :int -> int list -> int ` that takes the number $N$ of slices in the pizza and a list of $N$ integer that contains the integers ($S_i$) that represent the score you attributed to each slice. These values follow these constraints: + + + + + + + + + + + + +
$1 \leq N \leq 5 000$ Number of slices.
$−100 \leq S_i \leq 100$ Value of each slice.
+ +The function returns an integer that is equal to the value of the best possible section of adjacent pizza slices. The smallest possible section would be a single slice. + +For instance, + +- `score 4 [2;-2;3;-1]` returns `3`. +- `score 16 [-1;1;3;-8;3;-2;5;10;-2;-5;4;1;-7;13;-8;4]` returns `20`. +- `score 4 [-1;-2;-3;-4]` returns `-1`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-pizzaria/meta.json b/exercises/smelodesousa/F5/5-pizzaria/meta.json new file mode 100644 index 0000000..8c1db99 --- /dev/null +++ b/exercises/smelodesousa/F5/5-pizzaria/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 3, + "title": "Luigi’s Pizzeria - From MIUP 2018", + "identifier": "5.23", + "authors": [["Rui Barata", "rui.barata@ubi.pt"]], + "backward_exercises": ["smelodesousa/F5/5-brackets"] +} diff --git a/exercises/smelodesousa/F5/5-pizzaria/prelude.ml b/exercises/smelodesousa/F5/5-pizzaria/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-pizzaria/prepare.ml b/exercises/smelodesousa/F5/5-pizzaria/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-pizzaria/solution.ml b/exercises/smelodesousa/F5/5-pizzaria/solution.ml new file mode 100644 index 0000000..1d10bd0 --- /dev/null +++ b/exercises/smelodesousa/F5/5-pizzaria/solution.ml @@ -0,0 +1,22 @@ +(* let score (n : int) (pizza : int list) = + let best_sum = ref (List.hd pizza) in + let current_sum = ref 0 in + let score_aux x = + current_sum := max x (!current_sum + x); + best_sum := max !best_sum !current_sum in + let () = List.iter score_aux pizza in + !best_sum *) + +(* Another possible solution *) +let score (n : int) (pizza : int list) : int = + let rec max_sum_aux (pizza : int list) (max_ending_here : int) + (max_so_far : int) : int = + match pizza with + | [] -> max_so_far + | x :: xs -> + max_sum_aux xs + (max 0 (max_ending_here + x)) + (max max_so_far (max_ending_here + x)) + in + max_sum_aux pizza 0 (-101) +(* minimum value is -100 *) diff --git a/exercises/smelodesousa/F5/5-pizzaria/template.ml b/exercises/smelodesousa/F5/5-pizzaria/template.ml new file mode 100644 index 0000000..26494d4 --- /dev/null +++ b/exercises/smelodesousa/F5/5-pizzaria/template.ml @@ -0,0 +1 @@ +let score n pizza = failwith "Unanswered" diff --git a/exercises/smelodesousa/F5/5-pizzaria/test.ml b/exercises/smelodesousa/F5/5-pizzaria/test.ml new file mode 100644 index 0000000..e1a3f16 --- /dev/null +++ b/exercises/smelodesousa/F5/5-pizzaria/test.ml @@ -0,0 +1,24 @@ +open Test_lib +open Report + +let sample_value () = + let () = Random.self_init () in + Random.int 201 - 100 + +let sample_score () = + let l = sample_list ~min_size:25 ~max_size:75 sample_value () in + (List.length l, l) + +let scoreS = + Section + ( [ Text "Testing function score" ], + test_function_2_against_solution [%ty: int -> int list -> int] "score" + ~sampler:sample_score ~gen:9 + [ + (1, [ -3 ]); + (4, [ 2; -2; 3; -1 ]); + (16, [ -1; 1; 3; -8; 3; -2; 5; 10; -2; -5; 4; 1; -7; 13; -8; 4 ]); + (4, [ -1; -2; -3; -4 ]); + ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ scoreS ] diff --git a/exercises/smelodesousa/F5/5-randomness-is-hard/descr.md b/exercises/smelodesousa/F5/5-randomness-is-hard/descr.md new file mode 100644 index 0000000..939ac50 --- /dev/null +++ b/exercises/smelodesousa/F5/5-randomness-is-hard/descr.md @@ -0,0 +1,46 @@ + + + + + +# Introduction + +Our next problem revolves around the following question: + +Given an array `v` of size `n` already initialized, how do we rearrange its elements in a simple way (i.e., not _very_ inefficiently) so that we end up with a shuffled version of the initial array? + +In other words, how do we find a permutation of its elements that seems random? + +The problem raised by this question seems simple, but in reality, it is not. Determining a permutation with good random properties in a simple way is not a straightforward problem to solve. + +In 1938, Ronald Fisher and Frank Yates, in the book _Statistical tables for biological, agricultural and medical research_, described a method that was later studied and brought to light by Donald Knuth himself... + +This method became known as *Knuth shuffle* or _Fisher-Yates-Knuth shuffle_. + +```ocaml +(* To shuffle an array v with n elements (indexes 0...n-1), do the following: *) + for i = n - 1 downto 1 do + let j = random int where 0 <= j <= i + swap v[j] and v[i] +``` + +The essential property of this method is that every possible permutation has the same probability of being returned by the algorithm, including the original permutation. + +# Objective + +Implement the function `knuth_shuffle: 'a array -> 'a array` that incorporates the algorithm presented above. Note that the received argument is the array we want to shuffle. The function `Random.int` from the OCaml module `Random` might be handy for this exercise (ref. https://caml.inria.fr/pub/docs/manual-ocaml/libref/Random.html). + +> Random.int : int -> int +> +> Random.int `bound` returns a random integer between $0$ (inclusive) +> and `bound` (exclusive). `bound` must be greater than $0$ and less than $2^30$. + +Note that it is *not expected* that you use functions such as `Random.init` or `Random.self_init`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-randomness-is-hard/meta.json b/exercises/smelodesousa/F5/5-randomness-is-hard/meta.json new file mode 100644 index 0000000..b3fdb67 --- /dev/null +++ b/exercises/smelodesousa/F5/5-randomness-is-hard/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Randomness is hard", + "identifier": "5.10", + "authors": [["Dário Santos", "dariovfsantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-lists-sublists-4"] +} diff --git a/exercises/smelodesousa/F5/5-randomness-is-hard/prelude.ml b/exercises/smelodesousa/F5/5-randomness-is-hard/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-randomness-is-hard/prepare.ml b/exercises/smelodesousa/F5/5-randomness-is-hard/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-randomness-is-hard/solution.ml b/exercises/smelodesousa/F5/5-randomness-is-hard/solution.ml new file mode 100644 index 0000000..6739eba --- /dev/null +++ b/exercises/smelodesousa/F5/5-randomness-is-hard/solution.ml @@ -0,0 +1,21 @@ +open Random + +let knuth_shuffle a = + Array.iteri + (fun i e -> + let j = Random.int (i + 1) in + a.(i) <- a.(j); + a.(j) <- e) + a; + a + +(* Alternate version with a regular loop: + let knuth_shuffle v = + for i = (Array.length v) - 1 downto 1 do + let j = Random.int (i+1) in + let aux = v.(i) in + v.(i) <- v.(j); + v.(j) <- aux + done; + v +*) diff --git a/exercises/smelodesousa/F5/5-randomness-is-hard/template.ml b/exercises/smelodesousa/F5/5-randomness-is-hard/template.ml new file mode 100644 index 0000000..01e38a8 --- /dev/null +++ b/exercises/smelodesousa/F5/5-randomness-is-hard/template.ml @@ -0,0 +1 @@ +let knuth_shuffle v = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-randomness-is-hard/test.ml b/exercises/smelodesousa/F5/5-randomness-is-hard/test.ml new file mode 100644 index 0000000..fff911c --- /dev/null +++ b/exercises/smelodesousa/F5/5-randomness-is-hard/test.ml @@ -0,0 +1,18 @@ +open Test_lib +open Report + +let random_state = ref (Random.get_state ()) + +let test_with_solution = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "knuth_shuffle" ], + test_function_1_against_solution [%ty: int array -> int array] + "knuth_shuffle" + ~before_reference:(fun _ -> random_state := Random.get_state ()) + ~before_user:(fun _ -> Random.set_state !random_state) + ~gen:17 + [ [||]; [| 1 |]; [| -5; 100; 125 |] ] ) + +let () = + set_result @@ ast_sanity_check code_ast @@ fun () -> [ test_with_solution ] diff --git a/exercises/smelodesousa/F5/5-rle/descr.md b/exercises/smelodesousa/F5/5-rle/descr.md new file mode 100644 index 0000000..09747d7 --- /dev/null +++ b/exercises/smelodesousa/F5/5-rle/descr.md @@ -0,0 +1,27 @@ +# Introduction + +Let's implement a classic and simple lossless data compression method known as a _run-length encoder_ (RLE). + +This method allows sequences of elements to be compressed and stored as a single data value and count. + +It is efficient when the considered sequences are known to have multiple repeated occurrences. It is used alongside other compression methods that create such repetitions (such as the _Burrows-Wheeler_ method) to compress images, FAX messages, and more. + +As an example, if we have a list with repeated characters: + +`aaiojanbeeebbaffssjjjjdreghsrf` is compressed to `a2iojanbe3b2af2s2j4dreghsrf`. + +The general rule applied to any sequence of characters is: character `x` of length `y` is substituted by `xy`, which means "`x`, `y` times". The RLE codification is a simple application of this basic rule. Decodification, which allows for the recreation of the original string, is simply this process reversed. + +# Goals + +1. Given an element `x` of an uncompressed list, we mean to define its image as per the RLE codification. If there is only one occurrence, then the codification should return `One x`, if it is the first element of a repetition of length `y`, it should return `Many (x, y)`. Which of these options correctly defines the type `rle_contents`: + + (a) `type rle_contents = int * (int*int)`
+ (b) `type rle_contents = One of int | Many of (int*int)`
+ (c) `type rle_contents = One | Many`
+ (d) `type rle_contents = { One : int; Many : int*int }`

+ +2. Define the function `rle_encode : int list -> rle_contents list` that calculates the codification of the list passed as a parameter. For example `rle_encode [1;1;3;3,3;2;5;5]` returns `[Many (1,2); Many (3,3); One 2; Many (5,2)]`. + +3. Define the inverse function `rle_decode : 'a rle_contents list -> int list`. +For example `rle_decode [Many (1,2); Many (3,3); One 2; Many (5,2)]` returns `[1;1;3;3,3;2;5;5]`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-rle/meta.json b/exercises/smelodesousa/F5/5-rle/meta.json new file mode 100644 index 0000000..8d2c05e --- /dev/null +++ b/exercises/smelodesousa/F5/5-rle/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 2, + "title" : "Run-length encoder", + "identifier" : "5.7", + "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], + "backward_exercises": ["mooc/week3/seq1/ex2"] +} diff --git a/exercises/smelodesousa/F5/5-rle/prelude.ml b/exercises/smelodesousa/F5/5-rle/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-rle/prepare.ml b/exercises/smelodesousa/F5/5-rle/prepare.ml new file mode 100644 index 0000000..dcb46bf --- /dev/null +++ b/exercises/smelodesousa/F5/5-rle/prepare.ml @@ -0,0 +1,6 @@ +type rle_contents = + One of int + | Many of (int*int) + +type choice = + | A | B | C | D | To_Answer of string \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-rle/solution.ml b/exercises/smelodesousa/F5/5-rle/solution.ml new file mode 100644 index 0000000..a54f8da --- /dev/null +++ b/exercises/smelodesousa/F5/5-rle/solution.ml @@ -0,0 +1,29 @@ +let p1 = B + +(* -------------------- ENCODER -------------------- *) +let rec countOccr x l = + match l with + [] -> 0 + | h::t -> if h=x then 1 + (countOccr x t) else 0 + +let rec split n l = + if n = 0 + then l + else match l with [] -> [] | h::t -> split (n-1) t + +let rec rle_encode l = + match l with + [] -> [] + | h::t -> let occur = countOccr h l in let new_list = split occur l in match occur with 1 -> (One h)::(rle_encode new_list) | _ -> (Many (h,occur))::(rle_encode new_list) + + +(* -------------------- DECODER -------------------- *) +let rec listBuilder x n = + match n with + 0 -> [] + | _ -> x::(listBuilder x (n-1)) + +let rec rle_decode l = + match l with + [] -> [] + | h::t -> match h with One x -> [x]@(rle_decode t) | Many (x,n) -> (listBuilder x n)@(rle_decode t) \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-rle/template.ml b/exercises/smelodesousa/F5/5-rle/template.ml new file mode 100644 index 0000000..d3c904b --- /dev/null +++ b/exercises/smelodesousa/F5/5-rle/template.ml @@ -0,0 +1,7 @@ +let p1 = To_Answer "Replace with your solution" + +let rle_encode l = + failwith "Replace with your solution" + +let rle_decode l = + failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-rle/test.ml b/exercises/smelodesousa/F5/5-rle/test.ml new file mode 100644 index 0000000..a6106a5 --- /dev/null +++ b/exercises/smelodesousa/F5/5-rle/test.ml @@ -0,0 +1,64 @@ +open Test_lib +open Report + +let rec sample_smal_list x n = +match n with +0 -> [] +| _ -> x::(sample_smal_list x (n-1)) + +let sample_code () = + let () = Random.self_init () in + let res = ref [] in + let () = + for i=0 to 21 do + res := (!res@(sample_smal_list (Random.int 10) ((Random.int 3) + 1))) + done in !res + +let sample_decode () = + let () = Random.self_init () in + let res = ref [] in + let () = + for i=0 to 21 do + let n = ((Random.int 3) + 1) in + match n with + | 1 -> res := (One (Random.int 10))::!res + | _ -> res := (Many ((Random.int 10), n))::!res + done in !res + +let ex1 = + set_progress "Grading exercise 1" ; + Section ([ Text "Exercise 1: " ; Code "" ], + test_variable_against_solution + [%ty: choice] + "p1") + +let rle_encodeS = + set_progress "Testing encoder function" ; + Section + ( + [ Text "Encoder: " ; Code "cases" ], + test_function_1_against_solution + [%ty: int list -> rle_contents list] + "rle_encode" + ~sampler: sample_code + ~gen: 6 + [[]; [1]; [1;1;1;5]; [5;1;1;1]] + ) + +let rle_decodeS = + set_progress "Testing decoder function" ; + Section + ( + [ Text "Decoder: " ; Code "cases" ], + test_function_1_against_solution + [%ty: rle_contents list -> int list] + "rle_decode" + ~sampler: sample_decode + ~gen: 7 + [[]; [One (0)]; [Many(1,5)]] + ) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [ ex1; rle_encodeS; rle_decodeS ] \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-salc/descr.md b/exercises/smelodesousa/F5/5-salc/descr.md new file mode 100644 index 0000000..c4e8d8a --- /dev/null +++ b/exercises/smelodesousa/F5/5-salc/descr.md @@ -0,0 +1,28 @@ + + + + + +In this exercise, we pretend to sort lists or arrays of integers with the help of `List.sort` or `Array.sort`, according to the provided criteria: + +1. Implement a function `sort1 : int array -> int array` that sorts the receiving array in descending order using the function `Array.sort`. + +2. Implement a function `sort2 : int list -> int list` that sorts the receiving list using the function `List.sort` and the following criteria: + + - Odd integers first (we are considering them to be lower than even numbers), even integers next; + - Odd numbers must be in descending order; + - Even numbers must be in ascending order. + +3. Implement a function `sort3 : int array -> int array` that sorts the receiving array using the last exercise's criteria, but this time with the help of `Array.sort`. + +4. Implement a function `sort4 : int list -> int list` that sorts the receiving list's integers by the lexicographic order of their values read backward. For example, let us consider $19$ and $111$. By comparing them from right to left, the $9$ from $19$ is greater than the $1$ from $111$. Consequently, we get that $19 > 111$ by comparing them in this particular order. + + Thus, `sort4 [121;17;191;32;19;91]` returns `[121;91;191;32;17;19]`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-salc/meta.json b/exercises/smelodesousa/F5/5-salc/meta.json new file mode 100644 index 0000000..1da19e6 --- /dev/null +++ b/exercises/smelodesousa/F5/5-salc/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Sorting arrays and lists according to certain criteria", + "identifier": "5.4", + "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-lotto"] +} diff --git a/exercises/smelodesousa/F5/5-salc/prelude.ml b/exercises/smelodesousa/F5/5-salc/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-salc/prepare.ml b/exercises/smelodesousa/F5/5-salc/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-salc/solution.ml b/exercises/smelodesousa/F5/5-salc/solution.ml new file mode 100644 index 0000000..b36142c --- /dev/null +++ b/exercises/smelodesousa/F5/5-salc/solution.ml @@ -0,0 +1,117 @@ +(* 1 *) +let sort1 a = + Array.sort (fun a b -> if a > b then -1 else if a < b then 1 else 0) a; + a + +(* 2 *) +let sort2 l = + let rec aux odd even = function + | h :: t -> + if h mod 2 <> 0 then aux (h :: odd) even t else aux odd (h :: even) t + | [] -> (odd, even) + in + let o, e = aux [] [] l in + List.sort (fun a b -> if a > b then -1 else if a < b then 1 else 0) o + @ List.sort (fun a b -> if a > b then 1 else if a < b then -1 else 0) e + +(* 3 *) +let sort3 a = + let rec aux a i odd even = + if i = -1 then (odd, even) + else if a.(i) mod 2 <> 0 then aux a (i - 1) (a.(i) :: odd) even + else aux a (i - 1) odd (a.(i) :: even) + in + let l_o, l_e = aux a (Array.length a - 1) [] [] in + let o, e = (Array.of_list l_o, Array.of_list l_e) in + Array.sort (fun a b -> if a > b then -1 else if a < b then 1 else 0) o; + Array.sort (fun a b -> if a > b then 1 else if a < b then -1 else 0) e; + Array.append o e + +(* 4 *) +let sort4 l = + let rec int_to_digits l = function + | 0 -> List.rev l + | n -> int_to_digits ((n mod 10) :: l) (n / 10) + in + let sort_fun a b = + let rec aux da db = + match (da, db) with + | ha :: ta, hb :: tb -> + if ha > hb then 1 else if ha < hb then -1 else aux ta tb + | [], _ :: _ -> -1 + | _ :: _, [] -> 1 + | [], [] -> 0 + in + let digA = int_to_digits [] a in + let digB = int_to_digits [] b in + aux digA digB + in + List.sort sort_fun l + +(* Original version: + (* 1 *) + let myCompare x y = if x < y then 1 else -1 + + let sort1 a = + Array.sort myCompare a; + a + + (* 2 *) + let myCompare_Odd x y = if x < y then 1 else -1 + + let myCompare_Even x y = if x < y then -1 else 1 + + let sort2 l : int list = + let l_Even = ref [] and l_Odd = ref [] in + begin + List.iter (fun x -> if (x mod 2) = 0 then + l_Even := !l_Even @ [x] + else + l_Odd := !l_Odd @ [x]) l; + + l_Odd := List.sort myCompare_Odd !l_Odd; + l_Even := List.sort myCompare_Even !l_Even; + !l_Odd @ !l_Even + end + + (* 3 *) + let myCompare_Odd x y = if x < y then 1 else -1 + + let myCompare_Even x y = if x < y then -1 else 1 + + let sort3 a : int array = + let l = ref [] in + let l_Even = ref [] and l_Odd = ref [] in + begin + l := Array.to_list a; + List.iter (fun x -> if (x mod 2) = 0 then + l_Even := !l_Even @ [x] + else + l_Odd := !l_Odd @ [x]) !l; + + l_Odd := List.sort myCompare_Odd !l_Odd; + l_Even := List.sort myCompare_Even !l_Even; + Array.of_list (!l_Odd @ !l_Even) + end + + (* 4 *) + let digits n = + let rec loop n acc = + if n = 0 then acc + else loop (n/10) (n mod 10::acc) in + match n with + | 0 -> [0] + | _ -> loop n [] + + let myCompare x y = + let x_list = List.rev (digits x) and y_list = List.rev (digits y) in + let value = ref(0) and flag = ref(true) in + let big_number = if List.length x_list > List.length y_list then 1 else -1 in + for i=0 to ((min (List.length x_list) (List.length y_list)) - 1) do + if List.nth x_list i > List.nth y_list i && !flag then ( value := 1; flag := false ) + else if List.nth x_list i < List.nth y_list i && !flag then ( value := -1; flag := false ); + done; if !value = 0 then big_number else !value + + let sort4 l = + List.sort myCompare l +*) diff --git a/exercises/smelodesousa/F5/5-salc/template.ml b/exercises/smelodesousa/F5/5-salc/template.ml new file mode 100644 index 0000000..1ba4471 --- /dev/null +++ b/exercises/smelodesousa/F5/5-salc/template.ml @@ -0,0 +1,4 @@ +let sort1 a = failwith "Unanswered" +let sort2 l = failwith "Unanswered" +let sort3 a = failwith "Unanswered" +let sort4 l = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-salc/test.ml b/exercises/smelodesousa/F5/5-salc/test.ml new file mode 100644 index 0000000..5f09c2b --- /dev/null +++ b/exercises/smelodesousa/F5/5-salc/test.ml @@ -0,0 +1,53 @@ +open Test_lib +open Report +open List +open Random + +let sort1T = + set_progress "Grading exercise 1"; + Section + ( [ Text "Exercise 1: "; Code "sort1" ], + test_function_1_against_solution [%ty: int array -> int array] "sort1" + ~sampler: + (sample_array ~min_size:15 ~max_size:45 ~dups:true (fun () -> + let () = Random.self_init () in + Random.int 100)) + ~gen:10 [] ) + +let sort2T = + set_progress "Grading exercise 2"; + Section + ( [ Text "Exercise 2: "; Code "sort2" ], + test_function_1_against_solution [%ty: int list -> int list] "sort2" + ~sampler: + (sample_list ~min_size:15 ~max_size:45 ~dups:true (fun () -> + let () = Random.self_init () in + Random.int 100)) + ~gen:10 [] ) + +let sort3T = + set_progress "Grading exercise 3"; + Section + ( [ Text "Exercise 3: "; Code "sort3" ], + test_function_1_against_solution [%ty: int array -> int array] "sort3" + ~sampler: + (sample_array ~min_size:15 ~max_size:45 ~dups:true (fun () -> + let () = Random.self_init () in + Random.int 100)) + ~gen:10 [] ) + +let sort4T = + set_progress "Grading exercise 4"; + Section + ( [ Text "Exercise 4: "; Code "sort4" ], + test_function_1_against_solution [%ty: int list -> int list] "sort4" + ~sampler: + (sample_list ~min_size:15 ~max_size:45 ~dups:true (fun () -> + let () = Random.self_init () in + Random.int 200)) + ~gen:10 + [ [ 121; 17; 191; 32; 19; 91 ] ] ) + +let () = + set_result @@ ast_sanity_check code_ast + @@ fun () -> [ sort1T; sort2T; sort3T; sort4T ] diff --git a/exercises/smelodesousa/F5/5-seq-true/descr.md b/exercises/smelodesousa/F5/5-seq-true/descr.md new file mode 100644 index 0000000..72be1ce --- /dev/null +++ b/exercises/smelodesousa/F5/5-seq-true/descr.md @@ -0,0 +1,16 @@ + + + + + +Using an iterator (e.g. `fold_left`, `map`, `for_all`, `iter`, `exists`, `filter`, etc.) define a function `max_seq : bool list -> int` which returns the length of the longest sequence of `true` from a list of booleans given in parameter. + +For example, `max_seq [true; true; false ; true; false; true ; true; true; true; false; false; true]` returns $4$. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-seq-true/meta.json b/exercises/smelodesousa/F5/5-seq-true/meta.json new file mode 100644 index 0000000..5538608 --- /dev/null +++ b/exercises/smelodesousa/F5/5-seq-true/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Maximum sequence of true", + "identifier": "5.8", + "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-rle"] +} diff --git a/exercises/smelodesousa/F5/5-seq-true/prelude.ml b/exercises/smelodesousa/F5/5-seq-true/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-seq-true/prepare.ml b/exercises/smelodesousa/F5/5-seq-true/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-seq-true/solution.ml b/exercises/smelodesousa/F5/5-seq-true/solution.ml new file mode 100644 index 0000000..ba2b085 --- /dev/null +++ b/exercises/smelodesousa/F5/5-seq-true/solution.ml @@ -0,0 +1,19 @@ +(* let max_seq lista_bool = + let max_seq_var = ref(0) and aux = ref(0) in + begin + List.iter (fun y -> if y then aux := !aux + 1 else ( if !aux > !max_seq_var then (max_seq_var := !aux); aux := 0)) lista_bool; + !max_seq_var + end *) + +(* Another solution *) +let max_seq (l : bool list) = + let max = ref 0 in + let current = ref 0 in + List.iter + (fun x -> + if x then ( + current := !current + 1; + if !current > !max then max := !current) + else current := 0) + l; + !max diff --git a/exercises/smelodesousa/F5/5-seq-true/template.ml b/exercises/smelodesousa/F5/5-seq-true/template.ml new file mode 100644 index 0000000..8bd1761 --- /dev/null +++ b/exercises/smelodesousa/F5/5-seq-true/template.ml @@ -0,0 +1 @@ +let max_seq lista_bool = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-seq-true/test.ml b/exercises/smelodesousa/F5/5-seq-true/test.ml new file mode 100644 index 0000000..a5e5d2c --- /dev/null +++ b/exercises/smelodesousa/F5/5-seq-true/test.ml @@ -0,0 +1,33 @@ +open Test_lib +open Report +open List +open Random + +let max_seqQ = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "max_seq" ], + test_function_1_against_solution [%ty: bool list -> int] "max_seq" + ~sampler: + (sample_list ~min_size:5 ~max_size:20 ~dups:true (fun () -> + let () = Random.self_init () in + Random.bool ())) + ~gen:10 + [ + [ + true; + true; + false; + true; + false; + true; + true; + true; + true; + false; + false; + true; + ]; + ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ max_seqQ ] diff --git a/exercises/smelodesousa/F5/5-shine-in-society/descr.md b/exercises/smelodesousa/F5/5-shine-in-society/descr.md new file mode 100644 index 0000000..162c48f --- /dev/null +++ b/exercises/smelodesousa/F5/5-shine-in-society/descr.md @@ -0,0 +1,55 @@ + + + + + +# Introduction + +After all, shining at a dinner party is easy, being the star of a _vernissage_ is as easy as eating a _canapé_. Simply apply this foolproof recipe. Randomly select one element from each column listed here and join them in the order of the sets to form a sentence... _et voilá_. + +For example, **"The diagnosis identifies the institutional blocks of common practice"**. + + +```ocaml +| Set 1 | Set 2 | Set 3 | Set 4 | Set 5 | +| :-------------------: | :-----------: | :----------------------: | :------------------: | :-------------------: | +| "The excellence" | "reinforces" | "the institutional" | "factors" | "of performance" | +| "The intervention" | "mobilizes" | "the organizational" | "processes" | "of the device" | +| "The goal" | "reveals" | "the qualitative" | "parameters" | "of the company" | +| "The diagnosis" | "stimulates" | "the analytical" | "progresses" | "of the group" | +| "The experimentation" | "modifies" | "the characteristic" | "concepts" | "of the beneficiaries" | +| "The formation" | "clarifies" | "the motivational" | "different know-hows" | "of the hierarchy" | +| "The evaluation" | "renews" | "the pedagogical" | "problems" | "of common practice" | +| "The purpose" | "identifies" | "the representative" | "indicators" | "of the procedures" | +| "The expression" | "perfects" | "the contributory" | "results" | "of the actors" | +| "The management" | "develops" | "the cumulative"; | "effects"; | "of the problems" | +| "The method" | "dynamizes" | "the strategic" | "blocks" | "of the structures" | +| "The experience" | "programs" | "the neuro-linguistic" | "prerequisites" | "of the meta-context" | +| "The reframing" | "scores" | "the systemic" | "paradoxes" | "of the organization" | +``` + +Assume that each set is organized in the form of an already declared vector, in the prelude, with the corresponding names: `v1`, `v2`, `v3`, `v4` and `v5`. + +# Goal + +The purpose of this exercise is to implement a function `speak_vacantly : unit -> string` that randomly produces a sentence, using the contents of the previously mentioned vectors. For example: `speak_vacantly () -> "The excellence perfects the cumulative different know-hows of the device"`. + + +For this purpose, you can use the function `Random.int` from the OCaml module `Random` (see https://caml.inria.fr/pub/docs/manual-ocaml/libref/Random.html) + + +> Random.int : int -> int +> +> Random.int bound returns a random integer between $0$ (inclusive) +> and `bound` (exclusive). `bound` must be greater than $0$ and less than $2^30$. + + +Using this function, you can obtain integers belonging to the range $\{0...12\}$. Please note that *it is not expected* that you use functions such as `Random.init` or `Random.self_init`. \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-shine-in-society/meta.json b/exercises/smelodesousa/F5/5-shine-in-society/meta.json new file mode 100644 index 0000000..8fe2f49 --- /dev/null +++ b/exercises/smelodesousa/F5/5-shine-in-society/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version" : "2", + "kind" : "exercise", + "stars" : 1, + "title" : "Shine in society", + "identifier" : "5.2", + "authors" : [["Dário Santos", "dariovfsantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-lists"] +} diff --git a/exercises/smelodesousa/F5/5-shine-in-society/prelude.ml b/exercises/smelodesousa/F5/5-shine-in-society/prelude.ml new file mode 100644 index 0000000..c5bdcaa --- /dev/null +++ b/exercises/smelodesousa/F5/5-shine-in-society/prelude.ml @@ -0,0 +1,35 @@ +let v1 = [| + "The excellence"; "The intervention"; "The goal"; "The diagnosis"; + "The experimentation"; "The formation"; "The evaluation"; "The purpose"; + "The expression"; "The management"; "The method"; "The experience"; + "The reframing" +|] + +let v2 = [| + "reinforces"; "mobilizes"; "reveals"; "stimulates"; + "modifies"; "clarifies"; "renews"; "identifies"; + "perfects"; "develops"; "dynamizes"; "programs"; + "scores" +|] + +let v3 = [| + "the institutional"; "the organizational"; "the qualitative"; "the analytical"; + "the characteristic"; "the motivational"; "the pedagogical"; "the representative"; + "the contributory"; "the cumulative"; "the strategic"; "the neuro-linguistic"; + "the systemic" +|] + +let v4 = [| + "factors"; "processes"; "parameters"; "progresses"; + "concepts"; "different know-hows"; "problems"; "indicators"; + "results"; "effects"; "blocks"; "prerequisites"; + "paradoxes" +|] + +let v5 = [| + "of performance"; "of the device"; "of the company"; "of the group"; + "of the beneficiaries"; "of the hierarchy"; "of common practice"; "of the procedures"; + "of the actors"; "of the problems"; "of the structures"; "of the meta-context"; + "of the organization" +|] + diff --git a/exercises/smelodesousa/F5/5-shine-in-society/prepare.ml b/exercises/smelodesousa/F5/5-shine-in-society/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-shine-in-society/solution.ml b/exercises/smelodesousa/F5/5-shine-in-society/solution.ml new file mode 100644 index 0000000..0694c2a --- /dev/null +++ b/exercises/smelodesousa/F5/5-shine-in-society/solution.ml @@ -0,0 +1,2 @@ +let speak_vacantly () = + v1.(Random.int 13)^" "^v2.(Random.int 13)^" "^v3.(Random.int 13)^" "^v4.(Random.int 13)^" "^v5.(Random.int 13) \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-shine-in-society/template.ml b/exercises/smelodesousa/F5/5-shine-in-society/template.ml new file mode 100644 index 0000000..39a34d4 --- /dev/null +++ b/exercises/smelodesousa/F5/5-shine-in-society/template.ml @@ -0,0 +1 @@ +let speak_vacantly () = failwith "Replace with your solution" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-shine-in-society/test.ml b/exercises/smelodesousa/F5/5-shine-in-society/test.ml new file mode 100644 index 0000000..ce76828 --- /dev/null +++ b/exercises/smelodesousa/F5/5-shine-in-society/test.ml @@ -0,0 +1,21 @@ +open Test_lib +open Report + +let random_state = ref (Random.get_state ()) + +let test_with_solution = + Section([Text "Tests"], + test_function_1_against_solution + [%ty: unit -> string] + "speak_vacantly" + ~before_reference: (fun _ -> random_state := Random.get_state ()) + ~before_user: (fun _ -> Random.set_state !random_state) + ~gen:20 + ~sampler: (fun () -> ()) + [] + ) + +let () = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + [test_with_solution] diff --git a/exercises/smelodesousa/F5/5-subsequence-of-lists/descr.md b/exercises/smelodesousa/F5/5-subsequence-of-lists/descr.md new file mode 100644 index 0000000..71beba0 --- /dev/null +++ b/exercises/smelodesousa/F5/5-subsequence-of-lists/descr.md @@ -0,0 +1,15 @@ + + + + + +Implement a function `subseq : 'a list -> 'a list -> bool` that determines if a list `w1` is a subsequence of another list `w2`. A list `l1` is a subsequence of a list `l2` if we can obtain `l1` from `l2` by removing $0$ or more elements from the latter. For example, `[4;7;5;1]` is a subsequence of `[4;5;4;6;2;7;5;6;8;1;0]`. +​ \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-subsequence-of-lists/meta.json b/exercises/smelodesousa/F5/5-subsequence-of-lists/meta.json new file mode 100644 index 0000000..7e811d2 --- /dev/null +++ b/exercises/smelodesousa/F5/5-subsequence-of-lists/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 3, + "title": "Subsequence of lists", + "identifier": "5.5", + "authors": [["Dário Santos", "dariovfsantos@gmail.com"]], + "backward_exercises": ["smelodesousa/F5/5-max-sub-list"] +} diff --git a/exercises/smelodesousa/F5/5-subsequence-of-lists/prelude.ml b/exercises/smelodesousa/F5/5-subsequence-of-lists/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-subsequence-of-lists/prepare.ml b/exercises/smelodesousa/F5/5-subsequence-of-lists/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-subsequence-of-lists/solution.ml b/exercises/smelodesousa/F5/5-subsequence-of-lists/solution.ml new file mode 100644 index 0000000..c2aee79 --- /dev/null +++ b/exercises/smelodesousa/F5/5-subsequence-of-lists/solution.ml @@ -0,0 +1,19 @@ +let subseq w1 w2 = + let rec aux l1 l2 = + match (l1, l2) with + | h1 :: t1, h2 :: t2 -> if h1 = h2 then aux t1 t2 else aux l1 t2 + | _ :: _, [] -> false + | [], _ -> true + in + aux w1 w2 + +(* Original version: + let subseq l1 l2 = + let rec iter_list l1 l2 = + try + match l2 with + | [] -> l1 + | h::t -> + iter_list (if h=(List.hd l1) then (List.tl l1) else l1) t + with _ -> l1 in + [] = (iter_list l1 l2) *) diff --git a/exercises/smelodesousa/F5/5-subsequence-of-lists/template.ml b/exercises/smelodesousa/F5/5-subsequence-of-lists/template.ml new file mode 100644 index 0000000..8a446bd --- /dev/null +++ b/exercises/smelodesousa/F5/5-subsequence-of-lists/template.ml @@ -0,0 +1 @@ +let subseq w1 w2 = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-subsequence-of-lists/test.ml b/exercises/smelodesousa/F5/5-subsequence-of-lists/test.ml new file mode 100644 index 0000000..6deb3af --- /dev/null +++ b/exercises/smelodesousa/F5/5-subsequence-of-lists/test.ml @@ -0,0 +1,41 @@ +open Test_lib +open Report + +(* n: maximum size of generated list *) +let generate_list n = + let sz = Random.int n + 1 in + let l = ref [] in + for i = 0 to sz - 1 do + l := (Random.int (2 * 9999) - 9999) :: !l + done; + (!l, sz) + +(* generates a sublist of l, of size sz, with a maximum size of n *) +let rec generate_sublist l = + let pick_number () = Random.int 100 > 40 in + match l with + | [] -> [] + | h :: t -> + if pick_number () then h :: generate_sublist t else generate_sublist t + +let sampler_subseq () = + match Random.int 100 with + | _ as x when x >= 30 -> + let l1, sz = generate_list 25 in + let l2 = generate_sublist l1 in + (l2, l1) + | _ -> + let l1, _ = generate_list 25 in + let l2, _ = generate_list 12 in + (l2, l1) + +let test_with_solution = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "subseq" ], + test_function_2_against_solution [%ty: int list -> int list -> bool] + "subseq" ~gen:19 ~sampler:sampler_subseq + [ ([], []); ([ 4; 7; 5; 1 ], [ 4; 5; 4; 6; 2; 7; 5; 6; 8; 1; 0 ]) ] ) + +let () = + set_result @@ ast_sanity_check code_ast @@ fun () -> [ test_with_solution ] diff --git a/exercises/smelodesousa/F5/5-zombie-attack/descr.md b/exercises/smelodesousa/F5/5-zombie-attack/descr.md new file mode 100644 index 0000000..a5b36bb --- /dev/null +++ b/exercises/smelodesousa/F5/5-zombie-attack/descr.md @@ -0,0 +1,51 @@ +# Introduction + +A zombie invasion has affected your neighborhood. Your neighborhood was doomed if not for the cats, which are inherently resistant to disease and zombie predators. + +The neighborhood is shaped like a board of *n* by *n*, with a person, cat, or zombie alternately occupying each cell. + +A zombie attacks its neighbors from above, below, right, and left. If a zombie attacks a man, it transforms into one afterward and begins to attack its neighbors (the ones above, below, left, and right). In addition, when a cat is attacked, the attack is simply canceled, and the cat continues to be a cat. +Lastly, when a zombie is attacked by another zombie, they look at each other with what is left of their eyes and cancel the attack with an apologetic grunt. + +Your task is: given a board filled with an initial configuration, find the final configuration. Will there be any survivors? + +An example of an initial configuration might be: + +![zombie-0](https://i.imgur.com/RXsUuzU.png) + +In this case, the final configuration is: + +![zombie-11](https://i.imgur.com/dbRhGAv.png) + + +# Objectives + +Define the function `zombie_attack : char array array -> char array array`, which receives the initial configuration in the form of a square array of characters, calculates and returns the final configuration. + +The characters that make up the matrix are alternatively the character * ("asterisk", representing a cell that harbors a zombie), the character X (representing a cell that contains a brave cat), or the character . ("dot", representing a cell that contains an innocent passer-by). + +The matrix will not be larger than 1000. + +Input examples: + +``` +*...*.. +..XX... +.X..X.. +..X..X. +X.X.X.. +.X.X... +X.....* +``` + +Output examples: + +``` +******* +**XX*** +*X..X** +**X..X* +X*X.X** +.X*X*** +X****** +``` \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-zombie-attack/meta.json b/exercises/smelodesousa/F5/5-zombie-attack/meta.json new file mode 100644 index 0000000..a36438e --- /dev/null +++ b/exercises/smelodesousa/F5/5-zombie-attack/meta.json @@ -0,0 +1,9 @@ +{ + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Zombie attack!", + "identifier": "5.11", + "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], + "backward_exercises": ["hferee/11_printable"] +} diff --git a/exercises/smelodesousa/F5/5-zombie-attack/prelude.ml b/exercises/smelodesousa/F5/5-zombie-attack/prelude.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-zombie-attack/prepare.ml b/exercises/smelodesousa/F5/5-zombie-attack/prepare.ml new file mode 100644 index 0000000..e69de29 diff --git a/exercises/smelodesousa/F5/5-zombie-attack/solution.ml b/exercises/smelodesousa/F5/5-zombie-attack/solution.ml new file mode 100644 index 0000000..8477122 --- /dev/null +++ b/exercises/smelodesousa/F5/5-zombie-attack/solution.ml @@ -0,0 +1,47 @@ +(* let movimento_possivel x y game_matrix = + let n = Array.length game_matrix in + if x > (n-1) || y > (n-1) || x < 0 || y < 0 then false + else true + + let rec zombie_attack game_matrix = + let trocas = ref (false) in + let n = Array.length game_matrix in + begin + for linhas = 0 to (n-1) do + for colunas = 0 to (n-1) do + match game_matrix.(linhas).(colunas) with + |'*' -> if movimento_possivel (linhas-1) (colunas) game_matrix && game_matrix.(linhas-1).(colunas) = '.' then (Printf.printf "Bruh momentum ";game_matrix.(linhas-1).(colunas) <- '*'; trocas := true); + if movimento_possivel (linhas+1) (colunas) game_matrix && game_matrix.(linhas+1).(colunas) = '.' then (game_matrix.(linhas+1).(colunas) <- '*'; trocas := true); + if movimento_possivel (linhas) (colunas-1) game_matrix && game_matrix.(linhas).(colunas-1) = '.' then (game_matrix.(linhas).(colunas-1) <- '*'; trocas := true); + if movimento_possivel (linhas) (colunas+1) game_matrix && game_matrix.(linhas).(colunas+1) = '.' then (game_matrix.(linhas).(colunas+1) <- '*'; trocas := true); + |_ -> () + done + done; + if !trocas then zombie_attack game_matrix else game_matrix + end *) + +(* Another possible solution *) +let rec zombie_attack (game_matrix : char array array) : char array array = + let rec zombie_attack_aux (game_matrix : char array array) (i : int) (j : int) + : unit = + if + i < 0 + || i >= Array.length game_matrix + || j < 0 + || j >= Array.length game_matrix.(0) + then () + else if game_matrix.(i).(j) = '*' then () + else if game_matrix.(i).(j) = 'x' then game_matrix.(i).(j) <- '*' + else ( + game_matrix.(i).(j) <- '*'; + zombie_attack_aux game_matrix (i + 1) j; + zombie_attack_aux game_matrix (i - 1) j; + zombie_attack_aux game_matrix i (j + 1); + zombie_attack_aux game_matrix i (j - 1)) + in + for i = 0 to Array.length game_matrix - 1 do + for j = 0 to Array.length game_matrix.(0) - 1 do + if game_matrix.(i).(j) = 'x' then zombie_attack_aux game_matrix i j + done + done; + game_matrix \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-zombie-attack/template.ml b/exercises/smelodesousa/F5/5-zombie-attack/template.ml new file mode 100644 index 0000000..96e3173 --- /dev/null +++ b/exercises/smelodesousa/F5/5-zombie-attack/template.ml @@ -0,0 +1 @@ +let rec zombie_attack game_matrix = failwith "Unanswered" \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-zombie-attack/test.ml b/exercises/smelodesousa/F5/5-zombie-attack/test.ml new file mode 100644 index 0000000..e2ab6b9 --- /dev/null +++ b/exercises/smelodesousa/F5/5-zombie-attack/test.ml @@ -0,0 +1,55 @@ +open Test_lib +open Report + +let gen_sample_matrix () = + let () = Random.self_init () in + let n = Random.int 9 + 7 in + let array_test = Array.make_matrix n n '.' in + let zombie_number = if n > 12 then 1 else 2 in + let cat_number = if n > 12 then 7 else 10 in + for i = 0 to zombie_number do + let x = Random.int (n - 1) and y = Random.int (n - 1) in + if array_test.(x).(y) = '.' then array_test.(x).(y) <- '*' + else + let flag = ref true in + while !flag do + let x2 = Random.int (n - 1) and y2 = Random.int (n - 1) in + if array_test.(x2).(y2) = '.' then ( + array_test.(x).(y) <- '*'; + flag := false) + done + done; + for i = 0 to cat_number do + let x = Random.int (n - 1) and y = Random.int (n - 1) in + if array_test.(x).(y) = '.' then array_test.(x).(y) <- 'X' + else + let flag = ref false in + while !flag do + let x2 = Random.int (n - 1) and y2 = Random.int (n - 1) in + if array_test.(x2).(y2) = '.' then ( + array_test.(x).(y) <- 'X'; + flag := false) + done + done; + array_test + +let zombie_attackK = + set_progress "Grading exercise"; + Section + ( [ Text "Testing function"; Code "zombie_attack" ], + test_function_1_against_solution + [%ty: char array array -> char array array] "zombie_attack" + ~sampler:gen_sample_matrix ~gen:10 + [ + [| + [| '*'; '.'; '.'; '.'; '*'; '.'; '.' |]; + [| '.'; '.'; 'X'; 'X'; '.'; '.'; '.' |]; + [| '.'; 'X'; '.'; '.'; 'X'; '.'; '.' |]; + [| '.'; '.'; 'X'; '.'; '.'; 'X'; '.' |]; + [| 'X'; '.'; 'X'; '.'; 'X'; '.'; '.' |]; + [| '.'; 'X'; '.'; 'X'; '.'; '.'; '.' |]; + [| 'X'; '.'; '.'; '.'; '.'; '.'; '*' |]; + |]; + ] ) + +let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [ zombie_attackK ] \ No newline at end of file diff --git a/exercises/smelodesousa/F5/5-zombie-attack/zombie-0.png b/exercises/smelodesousa/F5/5-zombie-attack/zombie-0.png new file mode 100644 index 0000000000000000000000000000000000000000..451fc0523f1c32854e0b56edd16bf0104f1984ba GIT binary patch literal 8514 zcmZ8`c{tQj+y9iE>@$`z$jo5I5DFPavdkDsm@szP*DPa=QVB6*8%vUqrLwOXOG(zq zUXisJM95Z_LVokSzvrL#{o`EcI_Em~Ip=%d=UzUaZ-VJH18z<*CjbE8HpJ@_0Dw~t zj3&>{#F%*_iJ~()W)B@>9RQ#Zv0Z&`ZFpQ0wO|P0`Pfkt-1_quydE(>aqo=0_hr_wJxENBOKYw0aT+GPG zxPJY*v9U257yHJ>hK7blb91wj!Wj=I(F}_8o7!_Vj+q)%b9E4niHQ*tWvh9C zI@!|paTDX`WA15D4)lh!)0FslnQ^-O6@_wNKB!8=#Ug_xt`qcs{rYvderb6Uy*8_H z4#j14U1(ukonQ>);$+f7^Ey}wqt$pWWB8uuAm248et3I+sN+IOo?Pr5Db@4bPt#?a zDiylAx?a6{B_<{|Jw07fQ2_)3U0hsjZEfY{N7Dk>_xe7pb{ zE5PUsz*m4dK@^Y+22?@-iNXLN6QEWc&?Ir1iwU5@0k{JK5P43A0GTw;oXQZrZf(v2 zch#2M=An1@gO%%!+Y_uP5UvK0u#lT)u6 zy31QXa#l=t`4Z}E&`1A!S+MYcAU|EJfDWv~JfW>g{FIn&dEr%mY23}0wxKAov8C~5 zljq$j_X8uy2VmQuJDUL*2v3-#yn=!=e-JRuu5^4Aa4(|LhWZ?Brh)(_&DblTjDLGw zTs3w>J4iJWP&Vg_3TLkp4*Ex5^p3g*ciRwASJ;?~iYnxRqKMBj>?to=Op7fMs%wJf zKFcj@VmY%x%f!ks?<;lApUP7Ofy)<6;mrb%T(r;z!|M?a)~{=7-QM@aHW0U7|8!_4 z|0Fe+T|jMBpYz-Fz2)L@2>oZQcqPy)%c4E+L8rw(^xos3Hxai`B3X{yWAeb?oGf>> zX%`zioP)#~Z)IB-6r1;+CIz6XM!sbJ^;jAFqDJd*Sx}wvDCc6ya$XwV-i{}TfNx)Y zf_iSgp7jx={Ldo5v91{;p3`JvCdA3peZJ*iQ}`{^xpPH9&K}lHbX}xEf+y+X7P*@H=FkGn9BF?dV;=zjH0xl`SdT_PM1>(7@=TC4NP~^(G2AK60<9y{sgs zKA4b0e-?xSS4DpWg`ppV+#}mk5zTY7gV<uJ?}?^BHe-<32Wx)DmP_OIZ!B*}1T37ZHHGd( z{n)?~YWpgl^##7$@K($%zX~!!g{v;@^^C9i2>ai+Z5+ubZrskd$`fjuHj(o?^{(Q^ zz^ME1sV(iFO5IWM&eP}qNEq?l`!^f8l%(GMF^&A?`deCU$j@BAeTT+rt?z9VA%Cy{7 z?*?1;o*@^G*Dlq!47K%SMdr@2xW~qiQKW-FjpsrY9h7CuLw;Fg+zI*H|29u+&Hl_J zYP_q-dB#FFr>H{H`#aWb{P@PNLMz=!!DCHsTf<0s(D&Iy$e|?5V(*P=&$QFA@vTiz z;L^;GH}P0`XMT!R*BdN)IDZM^Q}cc928Fpv(y*9sZ|)hjSaRPzUvH#=%O&F%xZi%<&-3_SB&%`NoeT$@T#%;>e^snga1*BqqtAf6-VtSr2$IY=89uiri@&ZG5zgt z-+n=&4_AXDHVi%yTYGLb!VDiSt{ZZ%Sb(m4{5^5K6^fUF9!Sfp2z=M#2sx3TeKmVO zEp%d2Qcli%sdn4VN0JxLVX>K5;;?6bruor>8QXS!3;ufTo`m}js$~M+Oz~yr(H~2$ zXO0e=R#BPAq>W#O2C!OoF&?i6iOjTKTtx>rH&L;SsChO6yyEsg65*JRaZ+`uToS!jv)o$(Njyq}V{yQgaBzZ{=@$$C-NF~s_e{}X zs)yQWS9;bHobFz!MogOavlodeQh%DW;&B3wq|e;T#@hi!9C1mkqrnA}_Zpn~H9zU) zxM+2OG`HdJ(~9y*9;6AUL|V-kU#t>(edV5L)sS^$VtJ!K&AF6K z<16o@bOizdoFI7T>_Wjr$)%3m&KkDuglnvKHrf9BUJ}$yw_};RggXSxE6tv;PJQJi zJ5%iP*}xoTt?fFBRUUVD9|f^WIf4^Z{WF|i#2K;|w# zD7)TW))s*#Eutg)wZG*E2OSY6Zo5iett@XCH7mdPbZS>jy&*FrW+v@-R-m-B-)>K18VVGC<78hO~HrUgFq3Du78yI+P)4y`6oQT>*^cSb(478RWK zP@VLBuQwv;wNqo6tD`|w8Lq|To1oS0yEmPCll$(c+`0R4-RmPev3hEgNvWglevtz! zx=z8sG7`1(RkEIU{>EpqMh(y3Bch_67b?B&UbW{1{u#-~ZL%YlFXew6#cy29$)ujY zDFQ;DpYnNGC<0@D?!0nXgnKS(-@Y{SyvGo>{ZnP}7A>OV(zW3AhcPcoMK|%CLrQgV z52_G(digX68etkgdA%a3yu}G$o%}QNGpYuS9JfG+UH1@Han1g3sD)bTVQgc*C(3kK z{DVCN*utr}4hgA+?nSeTa5{(PL+NbXd8al3r^L;3+pZ21(4}CDGq!MU#(d62eGNkff06w z4#c_E!g$&BNK|~M<3-NsNobv1>G_sQ9+}56%34?63wbGRMh3Xp7<8qR7s|YE%VdOi z3@AF4qWl<e;uv1tuOZuR7>o4qS$d=ah`;B2}%0!hNdcza*+lj z7SMgz9H*M6{b(JwLtUPuDmo@<5Z)0dsPs;#R?X;)jY1tvs!8(V0z$&)Yz&Zefn}Rn z4ew}EjQWsP|H~+xUCeLy?Xm~uaX4$2kQ7_z`cc}M1ldrKo;o?Sdq}asrMTFY@>`E~ z3d(ThCkl1OZzC2YddcDR@aW`=7Rj(Y-%pEty0E|^jwez{!eyP76p8}Ihqu)<@@d~(Nz z*)}G?Z9|L@Uqu@^@tZ5hvMayTk)N&zZ)QVf|0S+7thJcKNvm`33@npd1KO-T3W#pG*O&tE8mq) zlQx9sA%wQOf>}E4`pl~MT4R#Jom-|TiUz3)AB_vcZ_s*0e)5bTO3?5wQ(k5@*Z>|@ zTCY}4c-msw`KsV#ZlcU~*M0EgDgeuNOajiiX;o$%!@lXzbrEk7v1SZxvXgG3f^;??@Es=7N}aqwm93?M_5(TlpOTg{wh0D zR8IR+kDq6t*;$sRrlksV`KoSoqF4H=NDd(bujo@aioD6;GtmwqQG|6Z)(VKyAuddP z4tehO(>sTPtLv$uoT|lY4CZ;CnIEmWs7>qLRlP!s|LGlVKURa%x=81^V_#d3b+<@Y za86W^VjQPrTP!2oEu?H`oTQwD#r&Ap6!N?=u8HePFU%hqXcw}sV>oiZ5LK?TJ&G0W z3=;Gvk__Wzjh@DE-^8=qJlWt=l;wT2C_Igoqv5gjU#-m^ ze)grl*#nmMT8OQPXN4DjH^lH89U^3Hi=S2r)&5WEdMhp*%wfzE8XKQ>;S>J!RUU91 z#Fp8h76Kbsw^t6bdMt|k)p3k?S$=CiD&te;!@k;$+?)@2IDw}pAIOL85+n`!rp6#- z`b~f~IjA95HOW=-oWZb`JJrQO1bI8G;nl}LZPSbtoWR|E!AktEblrjIm0kP>8*dUk z$HV1JFyDqZ_AzSH`hLz~Ah7SaBq&fj`7?xpox7C?2hEbXp5Q)Z)V|AL)`RJ>V|H*~ z#mU#eWw#>gncb;(zuY1F3!Hj9e#HGw5oE80a$<(Lih#A2cVv@fe`}PzKR0V`V1Y^+ zf>`fnNsL6=&s$Vw(daIk6$R_VQ!@K5c0V7e^&O|Ori)7M!@7fxAw(gV+xkoDft190 zc?f8)x*r2n`?xcm65y&)m?Da$I8)<9Z!m{*F(MvEZ}ASdH`|b#3=R5El=`OYuwl^ZeJ=IaCp>vB>cCFt zCg5i!M{;?QzzDv|;k8i4$VYrd*4h0BNecA&EW77s%=Y~3%=Lxy6_Oj|T%4{w@l_0W z7Lbl(PJ(9~*5atJM~8vwc~nA>JvFC>mE{v2!6D|Sqw58?4$@{=FC>(>o(Pmu5fG1C zhkSN!mNZA#k=(*8abn{cx9x7eZ_%9%-~=0XPWR70n~NIxg!f~-1DhKwzuCF_{wT*$ zNiWw`1nj!^czT_iB>leKXo+?J8|qZez&dnbXticZ1>SuoT8Sszz9lrRV+_z>HyF(F zTxO*cP86IHWKUOGn{IlhLj za?k)9&Q4AFleED@hQ*%X1oA^Jp(K5BF_k?qlP#(1E)5xt6QGwl+(Thrl@9ZLcDL$V)UO$y$yfuD_Q2Y$5+`ti zmL{nEcmjry_`+|yIKv~I@D!$J&kFQv5h(J?kc%|DjCnKo!19oPqw&#nfR$AxE96tAR6jJTt!^Y}+cob1d z#jz!lW8UBBQI}aa3|LjtW)V2DA*|Len@wEuUr|w30*+474(>WOJyJ;Lp|Z>3xuKTAMu{qka6m|iH(+VU6W+C)Tq zn`r$Je3PM613t=d+Jv(fXb|yQvI-HS0FB0Vm~e6|XQ}YDxeC~{CW)|I*p6w093n~V zP9!U*9izWG1bnI^9q~6_LDvM+*9du|l6Ii_MKn#fRd-`Q@%!XQ)?Die>ERtG zt(b5^`I>^UkufG)38n4@kIrLWN!67bi<;Ksy#Jt2y&^(^&S5EXqBmQ_hr<|dztW)& z*QyM?jw0Zh+qx{y(%20#USA-z-mAdvm}BegP##u7d_(8GV~$e4tJCp7LVGbE#gWi> zApO1|m#JN-y-P5-zD+9unrGTVccauS^BHsMASkRdv2(M$9}!O-brDzeF@{)JZ6DY$ zhTAzz_<0AnakUMvSEv%IyzBosT7dUb%ea<*D3uQ#SGvJg0!EL)P zRf9T(<`K=2`ku{_>zuN{pz=q>7FtIFr5xV;cNqGS0@WB|F?~%Ci1dDdwl9JGLORT0 zo_WeM-kK|77}88LFOMka--oYPCXkIW2`-cohDo0z*dpx{WMcKVjWp&P)_k3e%^9pA zAJ~xfhyiw;pXL|hrB21}eC@@s3H1r_>%#gn9G&^~PQ%nSrzBYVgtkN0pdX}SaA{n~ zVsY!%J(}C5X)_;_J_ zT+x-b?+>-Z|!@j#*v(C)B64Mh8#3r*4!@W-)j&=I_0eI8)GbI^EzUv z&B%bW_KBkavdiN40Lqi10L99&6aCS?)L$7(TS^2BIHu z2aD~!u88a+m_tE&i9*x{c48Cc<`o*dCafFY2)GJs?z{|ZuMJuXk&}6Ekq#sU1f&iq z>J+EK0KtlF#X70Fn4>f^f0ex&>@$X!NXRnEZ1G_01X(p$Y}Mj#bI^U0BHiNDQ!=ra z9|ECRsxBlp?zG0`Y)@=OIcmUXpvu*Pv=I_5yaH0^YwKF|N1a2$qaB@*iCmtZDDXC! zRFyh#Hxnfjd)6E;xgwYjq%UV3e2;u6V_ocqde(Ir!(U^Qm4d6w9WL9*Tv`IG5X@etyLbU90TT)?gf+hUEx_4~SeDA>ldLY8KX#jP)$BL5fqhbihS_2~1Nz`qAx{NNoI47fg|`Up^lfI($^RJN-yR;ecCPe zAvC3IfV?dEly%|3asjymDY52+cHM&jhcobz-@Xh4?i{GdOXSb}YcEI0b{7?M-nKm4 zQi0~(5iyN_Mjc2xozilDea}qN$uW);Vvh3k8S0r38 zcDRV#VkF2tc#XDHrnOGb~S{e#=(g`=($+C-Tqrpl5+h0GZN-O91WLdvpWsOBd+veH4#Sd+pQ8 zDrxWzoT-k%ThZudOz{dH8jen@_DJ*x~t>R0f7rX$iW(E3;<~iu6u5<3TN}^#8^V7tbL0k207pob(FSrkb z9O-Qr-~R{XQ!jJd;yqcBL}#Sa%^X<3`%y<6BHo9qd`AaCbrG8n@{dQfHxvZB7h3K? zyg(~uKYth8s%|=_2W^@7?S7bJyI)QdaGcG$e%~X~@pkCi`g*-EPV@MWw&qJ3$APfN z1S$>JL4A-X=R3Hd+~j@XQ@9~en`Y9r9ad-=u3Z>n9au;)a)MrX%MNs zTj3wB>tKoD7~1D_BE`==S!U3084EuiTnJ!&H@Q4)B5~=~-aksZ$rSGB$UreKcD9n; zgJep;d~IoN`S4z`IV#JNf74r_6c3MO4ys`Ff=hHCgSWaBFLfL5Q52sxi+^jSAv$_M z7Y+D97L&ErzV)E#LHxMm29vN(w!b9t1@lbG| zOcH(n+HutG=G`OStn_i>0qo?u&YzB zn}!R?jnM(lD=DifC@CwPS2kBx);N#WP`-dtQqoXTdh=AJ<$np>^mp}gzx96w)HPJq X{=dNQz#cb-0KgD?O}|R_M)dy!MfH;$ literal 0 HcmV?d00001 diff --git a/exercises/smelodesousa/F5/5-zombie-attack/zombie-11.png b/exercises/smelodesousa/F5/5-zombie-attack/zombie-11.png new file mode 100644 index 0000000000000000000000000000000000000000..019008799216517677d800441246b01b026c3743 GIT binary patch literal 8872 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 literal 0 HcmV?d00001 From 702af853640afb4ed670dea87c7f301e95df0fa6 Mon Sep 17 00:00:00 2001 From: Mohamed Hernouf Date: Mon, 5 Jun 2023 15:29:57 +0200 Subject: [PATCH 3/8] Ordering exercises --- exercises/fpottier/alpha_beta/meta.json | 3 +- exercises/fpottier/anagrams/meta.json | 3 +- exercises/fpottier/breaking_sort/meta.json | 3 +- exercises/fpottier/counting_trees/meta.json | 3 +- .../fpottier/enumerating_trees/meta.json | 3 +- exercises/fpottier/generic_sorting/meta.json | 3 +- exercises/fpottier/huffman/meta.json | 3 +- exercises/fpottier/infinite_arrays/meta.json | 3 +- exercises/fpottier/leftist_heaps/meta.json | 3 +- exercises/fpottier/merge_sort/meta.json | 3 +- .../fpottier/nondet_monad_cont/meta.json | 3 +- .../fpottier/nondet_monad_defun/meta.json | 3 +- exercises/fpottier/nondet_monad_seq/meta.json | 3 +- .../fpottier/parser_combinators/meta.json | 3 +- .../fpottier/persistent_arrays/meta.json | 3 +- exercises/fpottier/pprint/meta.json | 3 +- .../fpottier/random_access_lists/meta.json | 3 +- exercises/fpottier/sat/meta.json | 3 +- exercises/fpottier/spectre/meta.json | 3 +- exercises/fpottier/stereo/meta.json | 3 +- .../symbolic_sequences_data/meta.json | 3 +- .../symbolic_sequences_objects/meta.json | 3 +- exercises/fpottier/tictactoe/meta.json | 3 +- exercises/fpottier/tree_iterators/meta.json | 3 +- exercises/fpottier/unionfind/meta.json | 3 +- exercises/index.json | 261 ++++++++++++------ exercises/mooc/week1/seq3/ex2/meta.json | 4 +- exercises/mooc/week1/seq4/ex1/meta.json | 4 +- exercises/mooc/week1/seq4/ex2/meta.json | 4 +- exercises/mooc/week2/seq1/ex1/meta.json | 6 +- exercises/mooc/week2/seq1/ex2/meta.json | 6 +- exercises/mooc/week2/seq2/ex1/meta.json | 4 +- exercises/mooc/week2/seq2/ex2/meta.json | 6 +- exercises/mooc/week2/seq3/ex1/meta.json | 6 +- exercises/mooc/week2/seq3/ex2/meta.json | 6 +- exercises/mooc/week2/seq4/ex1/meta.json | 6 +- exercises/mooc/week3/seq1/ex1/meta.json | 6 +- exercises/mooc/week3/seq1/ex2/meta.json | 6 +- exercises/mooc/week3/seq2/ex1/meta.json | 6 +- exercises/mooc/week3/seq2/ex2/meta.json | 6 +- exercises/mooc/week3/seq3/ex1/meta.json | 6 +- exercises/mooc/week3/seq4/ex1/meta.json | 6 +- exercises/mooc/week3/seq4/ex3/meta.json | 6 +- exercises/mooc/week4/seq1/ex1/meta.json | 6 +- exercises/mooc/week4/seq2/ex1/meta.json | 6 +- exercises/mooc/week4/seq3/ex1/meta.json | 6 +- exercises/mooc/week4/seq3/ex2/meta.json | 6 +- exercises/mooc/week4/seq4/ex1/meta.json | 6 +- exercises/mooc/week5/seq1/ex1/meta.json | 4 +- exercises/mooc/week5/seq1/ex2/meta.json | 4 +- exercises/mooc/week5/seq2/ex1/meta.json | 4 +- exercises/mooc/week5/seq2/ex2/meta.json | 4 +- exercises/mooc/week5/seq3/ex1/meta.json | 4 +- exercises/mooc/week5/seq3/ex2/meta.json | 4 +- exercises/mooc/week5/seq4/ex1/meta.json | 4 +- exercises/mooc/week5/seq4/ex2/meta.json | 4 +- exercises/mooc/week6/seq1/ex1/meta.json | 4 +- exercises/mooc/week6/seq1/ex2/meta.json | 4 +- exercises/mooc/week6/seq2/ex1/meta.json | 4 +- exercises/mooc/week6/seq3/ex1/meta.json | 4 +- 60 files changed, 354 insertions(+), 154 deletions(-) diff --git a/exercises/fpottier/alpha_beta/meta.json b/exercises/fpottier/alpha_beta/meta.json index 0665b1b..e4cd1a9 100644 --- a/exercises/fpottier/alpha_beta/meta.json +++ b/exercises/fpottier/alpha_beta/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Alpha-Beta Search" + "title":"Alpha-Beta Search", + "backward_exercises": ["mooc/week6/seq3/ex1"] } diff --git a/exercises/fpottier/anagrams/meta.json b/exercises/fpottier/anagrams/meta.json index 98e7b4c..b84b137 100644 --- a/exercises/fpottier/anagrams/meta.json +++ b/exercises/fpottier/anagrams/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Recognizing Anagrams" + "title":"Recognizing Anagrams", + "backward_exercises": ["fpottier/alpha_beta"] } diff --git a/exercises/fpottier/breaking_sort/meta.json b/exercises/fpottier/breaking_sort/meta.json index b30c53b..b11b30e 100644 --- a/exercises/fpottier/breaking_sort/meta.json +++ b/exercises/fpottier/breaking_sort/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Breaking a Sort" + "title":"Breaking a Sort", + "backward_exercises": ["fpottier/anagrams"] } diff --git a/exercises/fpottier/counting_trees/meta.json b/exercises/fpottier/counting_trees/meta.json index cb3dd81..18f6160 100644 --- a/exercises/fpottier/counting_trees/meta.json +++ b/exercises/fpottier/counting_trees/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Counting trees" + "title":"Counting trees", + "backward_exercises": ["fpottier/breaking_sort"] } diff --git a/exercises/fpottier/enumerating_trees/meta.json b/exercises/fpottier/enumerating_trees/meta.json index 9c50f08..f382d19 100644 --- a/exercises/fpottier/enumerating_trees/meta.json +++ b/exercises/fpottier/enumerating_trees/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Enumerating Trees" + "title":"Enumerating Trees", + "backward_exercises": ["fpottier/counting_trees"] } diff --git a/exercises/fpottier/generic_sorting/meta.json b/exercises/fpottier/generic_sorting/meta.json index e6e1076..65fd4be 100644 --- a/exercises/fpottier/generic_sorting/meta.json +++ b/exercises/fpottier/generic_sorting/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Generic Sorting" + "title":"Generic Sorting", + "backward_exercises": ["fpottier/enumerating_trees"] } diff --git a/exercises/fpottier/huffman/meta.json b/exercises/fpottier/huffman/meta.json index 1a88fa4..02dc00b 100644 --- a/exercises/fpottier/huffman/meta.json +++ b/exercises/fpottier/huffman/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Huffman Compression" + "title":"Huffman Compression", + "backward_exercises": ["fpottier/generic_sorting"] } diff --git a/exercises/fpottier/infinite_arrays/meta.json b/exercises/fpottier/infinite_arrays/meta.json index d95a2fa..44b1b8a 100644 --- a/exercises/fpottier/infinite_arrays/meta.json +++ b/exercises/fpottier/infinite_arrays/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Infinite Arrays" + "title":"Infinite Arrays", + "backward_exercises": ["fpottier/huffman"] } diff --git a/exercises/fpottier/leftist_heaps/meta.json b/exercises/fpottier/leftist_heaps/meta.json index 1507b5b..c49d422 100644 --- a/exercises/fpottier/leftist_heaps/meta.json +++ b/exercises/fpottier/leftist_heaps/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Leftist heaps" + "title":"Leftist heaps", + "backward_exercises": ["fpottier/infinite_arrays"] } diff --git a/exercises/fpottier/merge_sort/meta.json b/exercises/fpottier/merge_sort/meta.json index fbce17f..30083ff 100644 --- a/exercises/fpottier/merge_sort/meta.json +++ b/exercises/fpottier/merge_sort/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Merge Sort" + "title":"Merge Sort", + "backward_exercises": ["fpottier/leftist_heaps"] } diff --git a/exercises/fpottier/nondet_monad_cont/meta.json b/exercises/fpottier/nondet_monad_cont/meta.json index b812599..7dd80dc 100644 --- a/exercises/fpottier/nondet_monad_cont/meta.json +++ b/exercises/fpottier/nondet_monad_cont/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Implementing Nondeterminism with Continuations" + "title":"Implementing Nondeterminism with Continuations", + "backward_exercises": ["fpottier/merge_sort"] } diff --git a/exercises/fpottier/nondet_monad_defun/meta.json b/exercises/fpottier/nondet_monad_defun/meta.json index b4807c9..f79e75d 100644 --- a/exercises/fpottier/nondet_monad_defun/meta.json +++ b/exercises/fpottier/nondet_monad_defun/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Implementing Nondeterminism as an Abstract Machine" + "title":"Implementing Nondeterminism as an Abstract Machine", + "backward_exercises": ["fpottier/nondet_monad_cont"] } diff --git a/exercises/fpottier/nondet_monad_seq/meta.json b/exercises/fpottier/nondet_monad_seq/meta.json index 89f6c65..e6724b3 100644 --- a/exercises/fpottier/nondet_monad_seq/meta.json +++ b/exercises/fpottier/nondet_monad_seq/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Implementing Nondeterminism with Sequences" + "title":"Implementing Nondeterminism with Sequences", + "backward_exercises": ["fpottier/nondet_monad_defun"] } diff --git a/exercises/fpottier/parser_combinators/meta.json b/exercises/fpottier/parser_combinators/meta.json index eece181..9e598c4 100644 --- a/exercises/fpottier/parser_combinators/meta.json +++ b/exercises/fpottier/parser_combinators/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Parser Combinators" + "title":"Parser Combinators", + "backward_exercises": ["fpottier/nondet_monad_seq"] } diff --git a/exercises/fpottier/persistent_arrays/meta.json b/exercises/fpottier/persistent_arrays/meta.json index 757fea6..727f461 100644 --- a/exercises/fpottier/persistent_arrays/meta.json +++ b/exercises/fpottier/persistent_arrays/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Persistent arrays" + "title":"Persistent arrays", + "backward_exercises": ["fpottier/parser_combinators"] } diff --git a/exercises/fpottier/pprint/meta.json b/exercises/fpottier/pprint/meta.json index 505c9ea..0e6a58a 100644 --- a/exercises/fpottier/pprint/meta.json +++ b/exercises/fpottier/pprint/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"A pretty-printer" + "title":"A pretty-printer", + "backward_exercises": ["fpottier/persistent_arrays"] } diff --git a/exercises/fpottier/random_access_lists/meta.json b/exercises/fpottier/random_access_lists/meta.json index 1037f2c..b2704c7 100644 --- a/exercises/fpottier/random_access_lists/meta.json +++ b/exercises/fpottier/random_access_lists/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Random access lists" + "title":"Random access lists", + "backward_exercises": ["fpottier/pprint"] } diff --git a/exercises/fpottier/sat/meta.json b/exercises/fpottier/sat/meta.json index 2f93339..d2ecf14 100644 --- a/exercises/fpottier/sat/meta.json +++ b/exercises/fpottier/sat/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"A SAT solver" + "title":"A SAT solver", + "backward_exercises": ["fpottier/random_access_lists"] } diff --git a/exercises/fpottier/spectre/meta.json b/exercises/fpottier/spectre/meta.json index 4d5814e..131ad53 100644 --- a/exercises/fpottier/spectre/meta.json +++ b/exercises/fpottier/spectre/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"From a Spectre to a Tree" + "title":"From a Spectre to a Tree", + "backward_exercises": ["fpottier/sat"] } diff --git a/exercises/fpottier/stereo/meta.json b/exercises/fpottier/stereo/meta.json index a54e532..d3afbb3 100644 --- a/exercises/fpottier/stereo/meta.json +++ b/exercises/fpottier/stereo/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Trees in Stereo Vision" + "title":"Trees in Stereo Vision", + "backward_exercises": ["fpottier/spectre"] } diff --git a/exercises/fpottier/symbolic_sequences_data/meta.json b/exercises/fpottier/symbolic_sequences_data/meta.json index 543b101..92c63fe 100644 --- a/exercises/fpottier/symbolic_sequences_data/meta.json +++ b/exercises/fpottier/symbolic_sequences_data/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Symbolic Sequences as Data" + "title":"Symbolic Sequences as Data", + "backward_exercises": ["fpottier/stereo"] } diff --git a/exercises/fpottier/symbolic_sequences_objects/meta.json b/exercises/fpottier/symbolic_sequences_objects/meta.json index 9e07e1f..14588f1 100644 --- a/exercises/fpottier/symbolic_sequences_objects/meta.json +++ b/exercises/fpottier/symbolic_sequences_objects/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Symbolic Sequences as Objects" + "title":"Symbolic Sequences as Objects", + "backward_exercises": ["fpottier/symbolic_sequences_data"] } diff --git a/exercises/fpottier/tictactoe/meta.json b/exercises/fpottier/tictactoe/meta.json index 8bd3b85..5145e3d 100644 --- a/exercises/fpottier/tictactoe/meta.json +++ b/exercises/fpottier/tictactoe/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Building a Game Tree" + "title":"Building a Game Tree", + "backward_exercises": ["fpottier/symbolic_sequences_objects"] } diff --git a/exercises/fpottier/tree_iterators/meta.json b/exercises/fpottier/tree_iterators/meta.json index c9ed7b6..003abff 100644 --- a/exercises/fpottier/tree_iterators/meta.json +++ b/exercises/fpottier/tree_iterators/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"Tree Iterators" + "title":"Tree Iterators", + "backward_exercises": ["fpottier/tictactoe"] } diff --git a/exercises/fpottier/unionfind/meta.json b/exercises/fpottier/unionfind/meta.json index 49d0b09..a8f07aa 100644 --- a/exercises/fpottier/unionfind/meta.json +++ b/exercises/fpottier/unionfind/meta.json @@ -2,5 +2,6 @@ "learnocaml_version":"1", "kind":"exercise", "stars":3, - "title":"The Union-Find data structure" + "title":"The Union-Find data structure", + "backward_exercises": ["fpottier/tree_iterators"] } diff --git a/exercises/index.json b/exercises/index.json index 18d34f5..abc5b8c 100644 --- a/exercises/index.json +++ b/exercises/index.json @@ -1,81 +1,188 @@ { - "learnocaml_version": "1", - "groups": - { - "mooc": - { - "title": "Introduction to Functional Programming (MOOC)", - "exercises" : [ - "mooc/week1/seq3/ex1", - "mooc/week1/seq3/ex2", - "mooc/week1/seq4/ex1", - "mooc/week1/seq4/ex2", - "mooc/week2/seq1/ex1", - "mooc/week2/seq1/ex2", - "mooc/week2/seq2/ex1", - "mooc/week2/seq2/ex2", - "mooc/week2/seq3/ex1", - "mooc/week2/seq3/ex2", - "mooc/week2/seq4/ex1", - "mooc/week3/seq1/ex1", - "mooc/week3/seq1/ex2", - "mooc/week3/seq2/ex1", - "mooc/week3/seq2/ex2", - "mooc/week3/seq3/ex1", - "mooc/week3/seq4/ex3", - "mooc/week3/seq4/ex1", - "mooc/week4/seq1/ex1", - "mooc/week4/seq2/ex1", - "mooc/week4/seq3/ex1", - "mooc/week4/seq3/ex2", - "mooc/week4/seq4/ex1", - "mooc/week5/seq1/ex1", - "mooc/week5/seq1/ex2", - "mooc/week5/seq2/ex1", - "mooc/week5/seq2/ex2", - "mooc/week5/seq3/ex1", - "mooc/week5/seq3/ex2", - "mooc/week5/seq4/ex1", - "mooc/week5/seq4/ex2", - "mooc/week6/seq1/ex1", - "mooc/week6/seq1/ex2", - "mooc/week6/seq2/ex1", - "mooc/week6/seq3/ex1", - "mooc/projects/klotski", - "mooc/projects/markov" - ] - }, - - "fpottier": - { - "title": "Advanced Functional Programming by François Pottier", - "exercises": [ - "fpottier/alpha_beta", - "fpottier/anagrams", - "fpottier/breaking_sort", - "fpottier/counting_trees", - "fpottier/enumerating_trees", - "fpottier/generic_sorting", - "fpottier/huffman", - "fpottier/infinite_arrays", - "fpottier/leftist_heaps", - "fpottier/merge_sort", - "fpottier/nondet_monad_cont", - "fpottier/nondet_monad_defun", - "fpottier/nondet_monad_seq", - "fpottier/parser_combinators", - "fpottier/persistent_arrays", - "fpottier/pprint", - "fpottier/random_access_lists", - "fpottier/sat", - "fpottier/spectre", - "fpottier/stereo", - "fpottier/symbolic_sequences_data", - "fpottier/symbolic_sequences_objects", - "fpottier/tictactoe", - "fpottier/tree_iterators", - "fpottier/unionfind" - ] + "learnocaml_version": "1", + "groups": { + "mooc": { + "title": "Introduction to Functional Programming (MOOC)", + "exercises": [ + "mooc/week1/seq3/ex1", + "mooc/week1/seq3/ex2", + "mooc/week1/seq4/ex1", + "mooc/week1/seq4/ex2", + "mooc/week2/seq1/ex1", + "mooc/week2/seq1/ex2", + "mooc/week2/seq2/ex1", + "mooc/week2/seq2/ex2", + "mooc/week2/seq3/ex1", + "mooc/week2/seq3/ex2", + "mooc/week2/seq4/ex1", + "mooc/week3/seq1/ex1", + "mooc/week3/seq1/ex2", + "mooc/week3/seq2/ex1", + "mooc/week3/seq2/ex2", + "mooc/week3/seq3/ex1", + "mooc/week3/seq4/ex3", + "mooc/week3/seq4/ex1", + "mooc/week4/seq1/ex1", + "mooc/week4/seq2/ex1", + "mooc/week4/seq3/ex1", + "mooc/week4/seq3/ex2", + "mooc/week4/seq4/ex1", + "mooc/week5/seq1/ex1", + "mooc/week5/seq1/ex2", + "mooc/week5/seq2/ex1", + "mooc/week5/seq2/ex2", + "mooc/week5/seq3/ex1", + "mooc/week5/seq3/ex2", + "mooc/week5/seq4/ex1", + "mooc/week5/seq4/ex2", + "mooc/week6/seq1/ex1", + "mooc/week6/seq1/ex2", + "mooc/week6/seq2/ex1", + "mooc/week6/seq3/ex1", + "mooc/projects/klotski", + "mooc/projects/markov" + ] + }, + "fpottier": { + "title": "Advanced Functional Programming by François Pottier", + "exercises": [ + "fpottier/alpha_beta", + "fpottier/anagrams", + "fpottier/breaking_sort", + "fpottier/counting_trees", + "fpottier/enumerating_trees", + "fpottier/generic_sorting", + "fpottier/huffman", + "fpottier/infinite_arrays", + "fpottier/leftist_heaps", + "fpottier/merge_sort", + "fpottier/nondet_monad_cont", + "fpottier/nondet_monad_defun", + "fpottier/nondet_monad_seq", + "fpottier/parser_combinators", + "fpottier/persistent_arrays", + "fpottier/pprint", + "fpottier/random_access_lists", + "fpottier/sat", + "fpottier/spectre", + "fpottier/stereo", + "fpottier/symbolic_sequences_data", + "fpottier/symbolic_sequences_objects", + "fpottier/tictactoe", + "fpottier/tree_iterators", + "fpottier/unionfind" + ] + }, + "practical_smelodesousa": { + "title": "Exercises by Simão Melo de Sousa", + "groups" : { + "types1" : { + "title": "Types, assessments and errors", + "exercises": [ + "smelodesousa/F1/1-type", + "smelodesousa/F1/1-type-error", + "smelodesousa/F1/1-mistery", + "smelodesousa/F1/1-future", + "smelodesousa/F1/1-errors", + "smelodesousa/F1/1-type-value", + "smelodesousa/F1/1-what-type", + "smelodesousa/F1/1-true-false", + "smelodesousa/F1/1-what-type2", + "smelodesousa/F1/1-type2", + "smelodesousa/F1/1-tuples", + "smelodesousa/F1/1-fun-calls" + ] + }, + "calculations": { + "title": "Calculations and recursion", + "exercises": [ + "smelodesousa/F3/3-simple-math", + "smelodesousa/F3/3-manhattan-distance", + "smelodesousa/F3/3-a-historic-algorithm", + "smelodesousa/F3/3-reach-infinity", + "smelodesousa/F3/3-pi", + "smelodesousa/F3/3-digits", + "smelodesousa/F3/3-seq-hofstadter", + "smelodesousa/F3/3-hofstadter", + "smelodesousa/F3/3-sums", + "smelodesousa/F3/3-tribonacci", + "smelodesousa/F3/3-fast-exponentiation", + "smelodesousa/F3/3-catalan", + "smelodesousa/F3/3-dichotomy", + "smelodesousa/F3/3-mccarthy-91-function", + "smelodesousa/F3/3-triangles", + "smelodesousa/F3/3-collatz-hailstones", + "smelodesousa/F3/3-stars-in-the-form-of-textual-fractals", + "smelodesousa/F3/3-how-many-mountains", + "smelodesousa/F3/3-dragon-greatness" + ] + }, + "types2": { + "title": "Types, assessments and errors - part 2", + "exercises": [ + "smelodesousa/F4/4-type-1", + "smelodesousa/F4/4-type-2", + "smelodesousa/F4/4-type-3", + "smelodesousa/F4/4-type-4", + "smelodesousa/F4/4-fun-1", + "smelodesousa/F4/4-fun-2" + ] + }, + "lists_vectors": { + "title": "Lists and vectors", + "exercises": [ + "smelodesousa/F5/5-lists", + "smelodesousa/F5/5-shine-in-society", + "smelodesousa/F5/5-lotto", + "smelodesousa/F5/5-salc", + "smelodesousa/F5/5-half", + "smelodesousa/F5/5-horner", + "smelodesousa/F5/5-rle", + "smelodesousa/F5/5-seq-true", + "smelodesousa/F5/5-lists-sublists-1", + "smelodesousa/F5/5-lists-sublists-2", + "smelodesousa/F5/5-lists-sublists-3", + "smelodesousa/F5/5-lists-sublists-4", + "smelodesousa/F5/5-randomness-is-hard", + "smelodesousa/F5/5-zombie-attack", + "smelodesousa/F5/5-absolute-majority", + "smelodesousa/F5/5-holand", + "smelodesousa/F5/5-gray-codes", + "smelodesousa/F5/5-max-sub-list", + "smelodesousa/F5/5-subsequence-of-lists", + "smelodesousa/F5/5-brackets", + "smelodesousa/F5/5-burrows-wheeler", + "smelodesousa/F5/5-pizzaria" + ] } + } + + }, + "practical_hferee" : { + "title" : "Functional Programming by Hugo Ferée", + "exercises": [ + "hferee/1.2_declarations", + "hferee/1.3_bool", + "hferee/1.4_conditionals", + "hferee/4.0_rock_paper_scissors", + "hferee/4.2_coordinates", + "hferee/5.0_prime_numbers", + "hferee/5.1_DNA", + "hferee/5.2_sierpinski_vg", + "hferee/6_hanoi", + "hferee/6_sierpinsky_ascii", + "hferee/7_lists", + "hferee/7_lists_binary_encoding", + "hferee/8_sieve", + "hferee/8_fold", + "hferee/8_sort", + "hferee/10_assoc", + "hferee/10_parameterized_lists", + "hferee/10_run_length", + "hferee/11_lists_lists", + "hferee/11_printable" + ] } + + } } diff --git a/exercises/mooc/week1/seq3/ex2/meta.json b/exercises/mooc/week1/seq3/ex2/meta.json index cfaa35a..0ad92ef 100644 --- a/exercises/mooc/week1/seq3/ex2/meta.json +++ b/exercises/mooc/week1/seq3/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "String Identifiers", - "stars": 1 } + "stars": 1, + "backward_exercises": ["mooc/week1/seq4/ex1"] +} diff --git a/exercises/mooc/week1/seq4/ex1/meta.json b/exercises/mooc/week1/seq4/ex1/meta.json index f95eb67..fabfc79 100644 --- a/exercises/mooc/week1/seq4/ex1/meta.json +++ b/exercises/mooc/week1/seq4/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Simple Functions over Integers", - "stars": 1 } + "stars": 1, + "backward_exercises" : ["hferee/1.2_declarations"] +} diff --git a/exercises/mooc/week1/seq4/ex2/meta.json b/exercises/mooc/week1/seq4/ex2/meta.json index 2dd5b86..912e3fe 100644 --- a/exercises/mooc/week1/seq4/ex2/meta.json +++ b/exercises/mooc/week1/seq4/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Simple Functions over Strings", - "stars": 1 } + "stars": 1, + "backward_exercises": ["mooc/week1/seq3/ex2"] +} diff --git a/exercises/mooc/week2/seq1/ex1/meta.json b/exercises/mooc/week2/seq1/ex1/meta.json index 8485c30..87990dc 100644 --- a/exercises/mooc/week2/seq1/ex1/meta.json +++ b/exercises/mooc/week2/seq1/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Tetragon", - "stars": 2.5 } + "title": "Tetragon", + "stars": 2.5, + "backward_exercises": ["smelodesousa/F4/4-type-4"] +} diff --git a/exercises/mooc/week2/seq1/ex2/meta.json b/exercises/mooc/week2/seq1/ex2/meta.json index 65fcaa6..7bda247 100644 --- a/exercises/mooc/week2/seq1/ex2/meta.json +++ b/exercises/mooc/week2/seq1/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Enigma", - "stars": 2.5 } + "title": "Enigma", + "stars": 2.5, + "backward_exercises": ["smelodesousa/F3/3-catalan"] +} diff --git a/exercises/mooc/week2/seq2/ex1/meta.json b/exercises/mooc/week2/seq2/ex1/meta.json index 9fb43b2..c0e611c 100644 --- a/exercises/mooc/week2/seq2/ex1/meta.json +++ b/exercises/mooc/week2/seq2/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Time on Planet Shadokus", - "stars": 2 } + "stars": 2, + "backward_exercises": ["hferee/4.2_coordinates"] +} diff --git a/exercises/mooc/week2/seq2/ex2/meta.json b/exercises/mooc/week2/seq2/ex2/meta.json index 17a6704..c3c5a7c 100644 --- a/exercises/mooc/week2/seq2/ex2/meta.json +++ b/exercises/mooc/week2/seq2/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Points and vectors", - "stars": 1.5 } + "title": "Points and vectors", + "stars": 1.5, + "backward_exercises": ["hferee/5.2_sierpinski_vg"] +} diff --git a/exercises/mooc/week2/seq3/ex1/meta.json b/exercises/mooc/week2/seq3/ex1/meta.json index 30cc904..4fcbfc3 100644 --- a/exercises/mooc/week2/seq3/ex1/meta.json +++ b/exercises/mooc/week2/seq3/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Searching for Strings in Arrays", - "stars": 2 } + "title": "Searching for Strings in Arrays", + "stars": 2, + "backward_exercises": ["smelodesousa/F3/3-dichotomy"] +} diff --git a/exercises/mooc/week2/seq3/ex2/meta.json b/exercises/mooc/week2/seq3/ex2/meta.json index 3762311..23b8827 100644 --- a/exercises/mooc/week2/seq3/ex2/meta.json +++ b/exercises/mooc/week2/seq3/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Finding the Minimum", - "stars": 1 } + "title": "Finding the Minimum", + "stars": 1, + "backward_exercises": ["mooc/week2/seq3/ex1"] +} diff --git a/exercises/mooc/week2/seq4/ex1/meta.json b/exercises/mooc/week2/seq4/ex1/meta.json index 49f936f..ebca152 100644 --- a/exercises/mooc/week2/seq4/ex1/meta.json +++ b/exercises/mooc/week2/seq4/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "A Small Typed Database", - "stars": 3 } + "title": "A Small Typed Database", + "stars": 3, + "backward_exercises": ["mooc/week2/seq2/ex1"] +} diff --git a/exercises/mooc/week3/seq1/ex1/meta.json b/exercises/mooc/week3/seq1/ex1/meta.json index 74eed7e..d73e87b 100644 --- a/exercises/mooc/week3/seq1/ex1/meta.json +++ b/exercises/mooc/week3/seq1/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "First In First Out", - "stars": 2 } + "title": "First In First Out", + "stars": 2, + "backward_exercises": ["hferee/8_sort"] +} diff --git a/exercises/mooc/week3/seq1/ex2/meta.json b/exercises/mooc/week3/seq1/ex2/meta.json index 0a10057..cd188c4 100644 --- a/exercises/mooc/week3/seq1/ex2/meta.json +++ b/exercises/mooc/week3/seq1/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Classic Functions Over Lists", - "stars": 2 } + "title": "Classic Functions Over Lists", + "stars": 2, + "backward_exercises": ["smelodesousa/F5/5-horner"] +} diff --git a/exercises/mooc/week3/seq2/ex1/meta.json b/exercises/mooc/week3/seq2/ex1/meta.json index 38acb5e..4d53677 100644 --- a/exercises/mooc/week3/seq2/ex1/meta.json +++ b/exercises/mooc/week3/seq2/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Symbolic Manipulation of Arithmetic Expressions", - "stars": 2 } + "title": "Symbolic Manipulation of Arithmetic Expressions", + "stars": 2, + "backward_exercises": ["smelodesousa/F5/5-randomness-is-hard"] +} diff --git a/exercises/mooc/week3/seq2/ex2/meta.json b/exercises/mooc/week3/seq2/ex2/meta.json index 458cbb5..f80d18e 100644 --- a/exercises/mooc/week3/seq2/ex2/meta.json +++ b/exercises/mooc/week3/seq2/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Tries", - "stars": 3 } + "title": "Tries", + "stars": 3, + "backward_exercises": ["smelodesousa/F5/5-pizzaria"] +} diff --git a/exercises/mooc/week3/seq3/ex1/meta.json b/exercises/mooc/week3/seq3/ex1/meta.json index 40d1a8e..f1ab797 100644 --- a/exercises/mooc/week3/seq3/ex1/meta.json +++ b/exercises/mooc/week3/seq3/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Type Directed Programming", - "stars": 1 } + "title": "Type Directed Programming", + "stars": 1, + "backward_exercises": ["mooc/week3/seq2/ex2"] +} diff --git a/exercises/mooc/week3/seq4/ex1/meta.json b/exercises/mooc/week3/seq4/ex1/meta.json index 4e348c9..d5002a0 100644 --- a/exercises/mooc/week3/seq4/ex1/meta.json +++ b/exercises/mooc/week3/seq4/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Balanced Binary Trees", - "stars": 1 } + "title": "Balanced Binary Trees", + "stars": 1, + "backward_exercises": ["smelodesousa/F4/4-fun-2"] +} diff --git a/exercises/mooc/week3/seq4/ex3/meta.json b/exercises/mooc/week3/seq4/ex3/meta.json index 3523823..077d3e5 100644 --- a/exercises/mooc/week3/seq4/ex3/meta.json +++ b/exercises/mooc/week3/seq4/ex3/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "An Implementation of List with an Efficient Concatenation", - "stars": 3 } + "title": "An Implementation of List with an Efficient Concatenation", + "stars": 3, + "backward_exercises": ["mooc/week3/seq4/ex1"] +} diff --git a/exercises/mooc/week4/seq1/ex1/meta.json b/exercises/mooc/week4/seq1/ex1/meta.json index 2dc60e8..ade925e 100644 --- a/exercises/mooc/week4/seq1/ex1/meta.json +++ b/exercises/mooc/week4/seq1/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Using First Class Functions", - "stars": 1 } + "title": "Using First Class Functions", + "stars": 1, + "backward_exercises": ["mooc/week3/seq4/ex3"] +} diff --git a/exercises/mooc/week4/seq2/ex1/meta.json b/exercises/mooc/week4/seq2/ex1/meta.json index ee6163c..9444ce1 100644 --- a/exercises/mooc/week4/seq2/ex1/meta.json +++ b/exercises/mooc/week4/seq2/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Functions Returning Functions", - "stars": 2 } + "title": "Functions Returning Functions", + "stars": 2, + "backward_exercises": ["mooc/week4/seq1/ex1"] +} diff --git a/exercises/mooc/week4/seq3/ex1/meta.json b/exercises/mooc/week4/seq3/ex1/meta.json index 3ac92ab..aec233c 100644 --- a/exercises/mooc/week4/seq3/ex1/meta.json +++ b/exercises/mooc/week4/seq3/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Optimizing Partial Applications", - "stars": 2.5 } + "title": "Optimizing Partial Applications", + "stars": 2.5, + "backward_exercises": ["mooc/week4/seq2/ex1"] +} diff --git a/exercises/mooc/week4/seq3/ex2/meta.json b/exercises/mooc/week4/seq3/ex2/meta.json index 1e008b8..4ca01c8 100644 --- a/exercises/mooc/week4/seq3/ex2/meta.json +++ b/exercises/mooc/week4/seq3/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "A Small Arithmetic Interpreter", - "stars": 2 } + "title": "A Small Arithmetic Interpreter", + "stars": 2, + "backward_exercises": ["mooc/week4/seq3/ex1"] +} diff --git a/exercises/mooc/week4/seq4/ex1/meta.json b/exercises/mooc/week4/seq4/ex1/meta.json index 9109123..6e08b79 100644 --- a/exercises/mooc/week4/seq4/ex1/meta.json +++ b/exercises/mooc/week4/seq4/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", - "title": "Using and Writing the Map Function", - "stars": 1 } + "title": "Using and Writing the Map Function", + "stars": 1, + "backward_exercises": ["mooc/week4/seq3/ex2"] +} diff --git a/exercises/mooc/week5/seq1/ex1/meta.json b/exercises/mooc/week5/seq1/ex1/meta.json index 3f055b9..4f7bddb 100644 --- a/exercises/mooc/week5/seq1/ex1/meta.json +++ b/exercises/mooc/week5/seq1/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Optimising a Tree Traversal using Exceptions", - "stars": 3 } + "stars": 3, + "backward_exercises": ["mooc/week4/seq4/ex1"] +} diff --git a/exercises/mooc/week5/seq1/ex2/meta.json b/exercises/mooc/week5/seq1/ex2/meta.json index 0aeb772..7ced5e9 100644 --- a/exercises/mooc/week5/seq1/ex2/meta.json +++ b/exercises/mooc/week5/seq1/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Unraveling the Automatic Grader", - "stars": 2 } + "stars": 2, + "backward_exercises": ["mooc/week5/seq1/ex1"] +} diff --git a/exercises/mooc/week5/seq2/ex1/meta.json b/exercises/mooc/week5/seq2/ex1/meta.json index 9fa3c7a..f3c50c2 100644 --- a/exercises/mooc/week5/seq2/ex1/meta.json +++ b/exercises/mooc/week5/seq2/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Printing Lists", - "stars": 1 } + "stars": 1, + "backward_exercises": ["mooc/week5/seq1/ex2"] +} diff --git a/exercises/mooc/week5/seq2/ex2/meta.json b/exercises/mooc/week5/seq2/ex2/meta.json index fa71131..0b7c97d 100644 --- a/exercises/mooc/week5/seq2/ex2/meta.json +++ b/exercises/mooc/week5/seq2/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Displaying a Filesystem Hierarchy", - "stars": 3 } + "stars": 3, + "backward_exercises": ["mooc/week5/seq2/ex1"] +} diff --git a/exercises/mooc/week5/seq3/ex1/meta.json b/exercises/mooc/week5/seq3/ex1/meta.json index c2b67ce..81fbc63 100644 --- a/exercises/mooc/week5/seq3/ex1/meta.json +++ b/exercises/mooc/week5/seq3/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Printing with Loops", - "stars": 1 } + "stars": 1, + "backward_exercises": ["mooc/week5/seq2/ex2"] +} diff --git a/exercises/mooc/week5/seq3/ex2/meta.json b/exercises/mooc/week5/seq3/ex2/meta.json index 469e12a..234c5d7 100644 --- a/exercises/mooc/week5/seq3/ex2/meta.json +++ b/exercises/mooc/week5/seq3/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Producing Fine ASCII Art", - "stars": 2 } + "stars": 2, + "backward_exercises": ["mooc/week5/seq3/ex1"] +} diff --git a/exercises/mooc/week5/seq4/ex1/meta.json b/exercises/mooc/week5/seq4/ex1/meta.json index 1a4fbfe..baabbdb 100644 --- a/exercises/mooc/week5/seq4/ex1/meta.json +++ b/exercises/mooc/week5/seq4/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Rotating the Contents of an Array", - "stars": 1.5 } + "stars": 1.5, + "backward_exercises": ["mooc/week5/seq3/ex2"] +} diff --git a/exercises/mooc/week5/seq4/ex2/meta.json b/exercises/mooc/week5/seq4/ex2/meta.json index 4fe8230..cc12c6c 100644 --- a/exercises/mooc/week5/seq4/ex2/meta.json +++ b/exercises/mooc/week5/seq4/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Implementing a Stack with an Array", - "stars": 1.5 } + "stars": 1.5, + "backward_exercises": ["mooc/week5/seq4/ex1"] +} diff --git a/exercises/mooc/week6/seq1/ex1/meta.json b/exercises/mooc/week6/seq1/ex1/meta.json index 915d1cd..f3b20fb 100644 --- a/exercises/mooc/week6/seq1/ex1/meta.json +++ b/exercises/mooc/week6/seq1/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Type Abstraction Using a Signature", - "stars": 1.5 } + "stars": 1.5, + "backward_exercises": ["mooc/week5/seq4/ex2"] +} diff --git a/exercises/mooc/week6/seq1/ex2/meta.json b/exercises/mooc/week6/seq1/ex2/meta.json index 0c78191..80d77f3 100644 --- a/exercises/mooc/week6/seq1/ex2/meta.json +++ b/exercises/mooc/week6/seq1/ex2/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Multisets", - "stars": 2 } + "stars": 2, + "backward_exercises": ["mooc/week6/seq1/ex1"] +} diff --git a/exercises/mooc/week6/seq2/ex1/meta.json b/exercises/mooc/week6/seq2/ex1/meta.json index cef94cc..8f0d44e 100644 --- a/exercises/mooc/week6/seq2/ex1/meta.json +++ b/exercises/mooc/week6/seq2/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Fixing a module signature", - "stars": 2 } + "stars": 2, + "backward_exercises": ["mooc/week6/seq1/ex2"] +} diff --git a/exercises/mooc/week6/seq3/ex1/meta.json b/exercises/mooc/week6/seq3/ex1/meta.json index f36b6de..2e88db4 100644 --- a/exercises/mooc/week6/seq3/ex1/meta.json +++ b/exercises/mooc/week6/seq3/ex1/meta.json @@ -1,4 +1,6 @@ { "learnocaml_version": "1", "kind": "exercise", "title": "Char Indexed Hashtables", - "stars": 2 } + "stars": 2, + "backward_exercises": ["mooc/week6/seq2/ex1"] +} From 25ec4226f2a042629c814942e074ba67258790f2 Mon Sep 17 00:00:00 2001 From: RadioPotin Date: Wed, 7 Jun 2023 14:51:56 +0200 Subject: [PATCH 4/8] add focus points to metadata of curated exercises --- exercises/hferee/1.2_declarations/meta.json | 16 +++++--- exercises/hferee/1.3_bool/meta.json | 16 +++++--- exercises/hferee/1.4_conditionals/meta.json | 16 +++++--- exercises/hferee/10_assoc/meta.json | 17 ++++++--- .../hferee/10_parameterized_lists/meta.json | 16 +++++--- exercises/hferee/10_run_length/meta.json | 15 +++++--- exercises/hferee/11_lists_lists/meta.json | 15 +++++--- exercises/hferee/11_printable/meta.json | 17 ++++++--- .../hferee/4.0_rock_paper_scissors/meta.json | 16 +++++--- exercises/hferee/4.2_coordinates/meta.json | 16 +++++--- exercises/hferee/5.0_prime_numbers/meta.json | 16 +++++--- exercises/hferee/5.1_DNA/meta.json | 15 +++++--- exercises/hferee/5.2_sierpinski_vg/meta.json | 16 +++++--- exercises/hferee/6_hanoi/meta.json | 16 +++++--- exercises/hferee/6_sierpinsky_ascii/meta.json | 16 +++++--- exercises/hferee/7_lists/meta.json | 16 +++++--- .../hferee/7_lists_binary_encoding/meta.json | 16 +++++--- exercises/hferee/8_fold/meta.json | 15 +++++--- exercises/hferee/8_sieve/meta.json | 15 +++++--- exercises/hferee/8_sort/meta.json | 15 +++++--- exercises/mooc/week1/seq3/ex1/meta.json | 12 ++++-- exercises/mooc/week1/seq3/ex2/meta.json | 13 +++++-- exercises/mooc/week1/seq4/ex1/meta.json | 12 ++++-- exercises/mooc/week1/seq4/ex2/meta.json | 12 ++++-- exercises/mooc/week2/seq1/ex1/meta.json | 11 +++++- exercises/mooc/week2/seq1/ex2/meta.json | 11 +++++- exercises/mooc/week2/seq2/ex1/meta.json | 13 +++++-- exercises/mooc/week2/seq2/ex2/meta.json | 11 +++++- exercises/mooc/week2/seq3/ex1/meta.json | 11 +++++- exercises/mooc/week2/seq3/ex2/meta.json | 11 +++++- exercises/mooc/week2/seq4/ex1/meta.json | 11 +++++- exercises/mooc/week3/seq1/ex1/meta.json | 11 +++++- exercises/mooc/week3/seq1/ex2/meta.json | 11 +++++- exercises/mooc/week3/seq2/ex1/meta.json | 11 +++++- exercises/mooc/week3/seq2/ex2/meta.json | 11 +++++- exercises/mooc/week3/seq3/ex1/meta.json | 11 +++++- exercises/smelodesousa/F1/1-errors/meta.json | 28 ++++++++++---- .../smelodesousa/F1/1-fun-calls/meta.json | 29 ++++++++++---- exercises/smelodesousa/F1/1-future/meta.json | 38 +++++++++++++++---- exercises/smelodesousa/F1/1-mistery/meta.json | 33 ++++++++++++---- .../smelodesousa/F1/1-true-false/meta.json | 17 +++++++-- exercises/smelodesousa/F1/1-tuples/meta.json | 17 +++++++-- .../smelodesousa/F1/1-type-error/meta.json | 17 +++++++-- .../smelodesousa/F1/1-type-value/meta.json | 32 ++++++++++++---- exercises/smelodesousa/F1/1-type/meta.json | 32 ++++++++++++---- exercises/smelodesousa/F1/1-type2/meta.json | 17 +++++++-- .../smelodesousa/F1/1-what-type/meta.json | 32 ++++++++++++---- .../smelodesousa/F1/1-what-type2/meta.json | 28 ++++++++++---- .../F3/3-a-historic-algorithm/meta.json | 19 ++++++++-- exercises/smelodesousa/F3/3-catalan/meta.json | 34 +++++++++++++---- .../F3/3-collatz-hailstones/meta.json | 37 ++++++++++++++---- .../smelodesousa/F3/3-dichotomy/meta.json | 22 +++++++++-- exercises/smelodesousa/F3/3-digits/meta.json | 29 ++++++++++---- .../F3/3-dragon-greatness/meta.json | 18 +++++++-- .../F3/3-fast-exponentiation/meta.json | 37 ++++++++++++++---- .../smelodesousa/F3/3-hofstadter/meta.json | 33 ++++++++++++---- .../F3/3-how-many-mountains/meta.json | 22 +++++++++-- .../F3/3-manhattan-distance/meta.json | 28 ++++++++++---- .../F3/3-mccarthy-91-function/meta.json | 33 ++++++++++++---- exercises/smelodesousa/F3/3-pi/meta.json | 19 ++++++++-- .../F3/3-reach-infinity/meta.json | 33 ++++++++++++---- .../F3/3-seq-hofstadter/meta.json | 16 +++++++- .../smelodesousa/F3/3-simple-math/meta.json | 18 +++++++-- .../meta.json | 22 +++++++++-- exercises/smelodesousa/F3/3-sums/meta.json | 29 ++++++++++---- .../smelodesousa/F3/3-triangles/meta.json | 29 ++++++++++---- .../smelodesousa/F3/3-tribonacci/meta.json | 19 ++++++++-- exercises/smelodesousa/F4/4-fun-1/meta.json | 32 ++++++++++++---- exercises/smelodesousa/F4/4-fun-2/meta.json | 32 ++++++++++++---- exercises/smelodesousa/F4/4-type-1/meta.json | 36 ++++++++++++++---- exercises/smelodesousa/F4/4-type-2/meta.json | 32 ++++++++++++---- exercises/smelodesousa/F4/4-type-3/meta.json | 17 +++++++-- exercises/smelodesousa/F4/4-type-4/meta.json | 17 +++++++-- .../F5/5-absolute-majority/meta.json | 29 ++++++++++---- .../smelodesousa/F5/5-brackets/meta.json | 24 ++++++++---- .../F5/5-burrows-wheeler/meta.json | 14 ++++++- .../smelodesousa/F5/5-gray-codes/meta.json | 29 ++++++++++---- exercises/smelodesousa/F5/5-half/meta.json | 28 ++++++++++---- exercises/smelodesousa/F5/5-holand/meta.json | 15 +++++++- exercises/smelodesousa/F5/5-horner/meta.json | 14 ++++++- .../F5/5-lists-sublists-1/meta.json | 24 ++++++++---- .../F5/5-lists-sublists-2/meta.json | 24 ++++++++---- .../F5/5-lists-sublists-3/meta.json | 24 ++++++++---- .../F5/5-lists-sublists-4/meta.json | 28 ++++++++++---- exercises/smelodesousa/F5/5-lists/meta.json | 24 ++++++++---- exercises/smelodesousa/F5/5-lotto/meta.json | 29 ++++++++++---- .../smelodesousa/F5/5-max-sub-list/meta.json | 14 ++++++- .../smelodesousa/F5/5-pizzaria/meta.json | 14 ++++++- .../F5/5-randomness-is-hard/meta.json | 14 ++++++- exercises/smelodesousa/F5/5-rle/meta.json | 29 ++++++++++---- exercises/smelodesousa/F5/5-salc/meta.json | 15 +++++++- .../smelodesousa/F5/5-seq-true/meta.json | 14 ++++++- .../F5/5-shine-in-society/meta.json | 25 ++++++++---- .../F5/5-subsequence-of-lists/meta.json | 14 ++++++- .../smelodesousa/F5/5-zombie-attack/meta.json | 15 +++++++- 95 files changed, 1500 insertions(+), 442 deletions(-) diff --git a/exercises/hferee/1.2_declarations/meta.json b/exercises/hferee/1.2_declarations/meta.json index 132c694..4870033 100644 --- a/exercises/hferee/1.2_declarations/meta.json +++ b/exercises/hferee/1.2_declarations/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 1, - "title": "Local declarations", - "backward_exercises": ["mooc/week1/seq3/ex1"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "focus": [ + "Variable declaration", + "int operators" + ], + "title": "Local declarations", + "backward_exercises": [ + "mooc/week1/seq3/ex1" + ] } diff --git a/exercises/hferee/1.3_bool/meta.json b/exercises/hferee/1.3_bool/meta.json index 54d00dd..c4a35b9 100644 --- a/exercises/hferee/1.3_bool/meta.json +++ b/exercises/hferee/1.3_bool/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 1, - "title": "Booleans", - "backward_exercises": ["mooc/week1/seq4/ex2"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Booleans", + "focus": [ + "Booleans", + "bool operators" + ], + "backward_exercises": [ + "mooc/week1/seq4/ex2" + ] } diff --git a/exercises/hferee/1.4_conditionals/meta.json b/exercises/hferee/1.4_conditionals/meta.json index b7726e1..9d50aaf 100644 --- a/exercises/hferee/1.4_conditionals/meta.json +++ b/exercises/hferee/1.4_conditionals/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "Conditional expressions", - "backward_exercises": ["hferee/1.3_bool"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Conditional expressions", + "focus": [ + "Booleans", + "conditional" + ], + "backward_exercises": [ + "hferee/1.3_bool" + ] } diff --git a/exercises/hferee/10_assoc/meta.json b/exercises/hferee/10_assoc/meta.json index 8935830..29827f0 100644 --- a/exercises/hferee/10_assoc/meta.json +++ b/exercises/hferee/10_assoc/meta.json @@ -1,7 +1,14 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "Option type and association lists", - "backward_exercises": ["hferee/10_parameterized_lists"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Option type and association lists", + "focus": [ + "List manipulation", + "option type", + "polymorphic types" + ], + "backward_exercises": [ + "hferee/10_parameterized_lists" + ] } diff --git a/exercises/hferee/10_parameterized_lists/meta.json b/exercises/hferee/10_parameterized_lists/meta.json index f2ecc46..e566e1b 100644 --- a/exercises/hferee/10_parameterized_lists/meta.json +++ b/exercises/hferee/10_parameterized_lists/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 1, - "title": "Parameterized lists", - "backward_exercises": ["mooc/week2/seq3/ex2"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Parameterized lists", + "focus": [ + "List manipulation", + "polymorphic types" + ], + "backward_exercises": [ + "mooc/week2/seq3/ex2" + ] } diff --git a/exercises/hferee/10_run_length/meta.json b/exercises/hferee/10_run_length/meta.json index a4d377b..a16be4d 100644 --- a/exercises/hferee/10_run_length/meta.json +++ b/exercises/hferee/10_run_length/meta.json @@ -1,7 +1,12 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 3, - "title": "Encode by range", - "backward_exercises": ["mooc/week3/seq2/ex1"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 3, + "title": "Encode by range", + "focus": [ + "List manipulation" + ], + "backward_exercises": [ + "mooc/week3/seq2/ex1" + ] } diff --git a/exercises/hferee/11_lists_lists/meta.json b/exercises/hferee/11_lists_lists/meta.json index c9dd666..fa411c1 100644 --- a/exercises/hferee/11_lists_lists/meta.json +++ b/exercises/hferee/11_lists_lists/meta.json @@ -1,7 +1,12 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "Lists of lists", - "backward_exercises": ["hferee/10_run_length"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Lists of lists", + "focus": [ + "List manipulation" + ], + "backward_exercises": [ + "hferee/10_run_length" + ] } diff --git a/exercises/hferee/11_printable/meta.json b/exercises/hferee/11_printable/meta.json index 3f0533f..919295b 100644 --- a/exercises/hferee/11_printable/meta.json +++ b/exercises/hferee/11_printable/meta.json @@ -1,7 +1,14 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "Display", - "backward_exercises": ["hferee/11_lists_lists"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Display", + "focus": [ + "List manipulation", + "polymorphic types", + "Print functions" + ], + "backward_exercises": [ + "hferee/11_lists_lists" + ] } diff --git a/exercises/hferee/4.0_rock_paper_scissors/meta.json b/exercises/hferee/4.0_rock_paper_scissors/meta.json index f4ec211..8f740f8 100644 --- a/exercises/hferee/4.0_rock_paper_scissors/meta.json +++ b/exercises/hferee/4.0_rock_paper_scissors/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 1, - "title": "Rock-Paper-Scissors", - "backward_exercises": ["mooc/week2/seq1/ex2"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Rock-Paper-Scissors", + "focus": [ + "Record", + "variant type" + ], + "backward_exercises": [ + "mooc/week2/seq1/ex2" + ] } diff --git a/exercises/hferee/4.2_coordinates/meta.json b/exercises/hferee/4.2_coordinates/meta.json index d2014da..8c64c68 100644 --- a/exercises/hferee/4.2_coordinates/meta.json +++ b/exercises/hferee/4.2_coordinates/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 1, - "title": "Change of coordinates", - "backward_exercises": ["hferee/4.0_rock_paper_scissors"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Change of coordinates", + "focus": [ + "Record", + "variant type" + ], + "backward_exercises": [ + "hferee/4.0_rock_paper_scissors" + ] } diff --git a/exercises/hferee/5.0_prime_numbers/meta.json b/exercises/hferee/5.0_prime_numbers/meta.json index d996a7a..a30d85e 100644 --- a/exercises/hferee/5.0_prime_numbers/meta.json +++ b/exercises/hferee/5.0_prime_numbers/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 1, - "title": "Prime numbers", - "backward_exercises": ["mooc/week2/seq4/ex1"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Prime numbers", + "focus": [ + "Arithmetic operations", + "recursive functions" + ], + "backward_exercises": [ + "mooc/week2/seq4/ex1" + ] } diff --git a/exercises/hferee/5.1_DNA/meta.json b/exercises/hferee/5.1_DNA/meta.json index ffa62a1..5eef167 100644 --- a/exercises/hferee/5.1_DNA/meta.json +++ b/exercises/hferee/5.1_DNA/meta.json @@ -1,7 +1,12 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "DNA sequences", - "backward_exercises": ["smelodesousa/F5/5-burrows-wheeler"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "DNA sequences", + "focus": [ + "String manipulation" + ], + "backward_exercises": [ + "smelodesousa/F5/5-burrows-wheeler" + ] } diff --git a/exercises/hferee/5.2_sierpinski_vg/meta.json b/exercises/hferee/5.2_sierpinski_vg/meta.json index 4212ced..9c2398d 100644 --- a/exercises/hferee/5.2_sierpinski_vg/meta.json +++ b/exercises/hferee/5.2_sierpinski_vg/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 3, - "title": "Sierpiński triangle", - "backward_exercises": ["hferee/5.0_prime_numbers"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 3, + "title": "Sierpiński triangle", + "focus": [ + "Arithmetic operations", + "floats" + ], + "backward_exercises": [ + "hferee/5.0_prime_numbers" + ] } diff --git a/exercises/hferee/6_hanoi/meta.json b/exercises/hferee/6_hanoi/meta.json index c59c108..9c77661 100644 --- a/exercises/hferee/6_hanoi/meta.json +++ b/exercises/hferee/6_hanoi/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 1, - "title": "Hanoi tower", - "backward_exercises": ["hferee/5.1_DNA"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 1, + "title": "Hanoi tower", + "focus": [ + "Variant type", + "Print functions" + ], + "backward_exercises": [ + "hferee/5.1_DNA" + ] } diff --git a/exercises/hferee/6_sierpinsky_ascii/meta.json b/exercises/hferee/6_sierpinsky_ascii/meta.json index 25008dc..8d75c74 100644 --- a/exercises/hferee/6_sierpinsky_ascii/meta.json +++ b/exercises/hferee/6_sierpinsky_ascii/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 3, - "title": "Sierpinsky in ascii", - "backward_exercises": ["hferee/6_hanoi"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 3, + "title": "Sierpinsky in ascii", + "focus": [ + "String manipulation", + "Print functions" + ], + "backward_exercises": [ + "hferee/6_hanoi" + ] } diff --git a/exercises/hferee/7_lists/meta.json b/exercises/hferee/7_lists/meta.json index f5b8776..513ab3e 100644 --- a/exercises/hferee/7_lists/meta.json +++ b/exercises/hferee/7_lists/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "Lists", - "backward_exercises": ["smelodesousa/F3/3-dragon-greatness"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Lists", + "focus": [ + "Variant type", + "list manipulation" + ], + "backward_exercises": [ + "smelodesousa/F3/3-dragon-greatness" + ] } diff --git a/exercises/hferee/7_lists_binary_encoding/meta.json b/exercises/hferee/7_lists_binary_encoding/meta.json index efd9c3a..5d85ecd 100644 --- a/exercises/hferee/7_lists_binary_encoding/meta.json +++ b/exercises/hferee/7_lists_binary_encoding/meta.json @@ -1,7 +1,13 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "Binary encoding of integers", - "backward_exercises": ["hferee/7_lists"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Binary encoding of integers", + "focus": [ + "Variant type", + "list manipulation" + ], + "backward_exercises": [ + "hferee/7_lists" + ] } diff --git a/exercises/hferee/8_fold/meta.json b/exercises/hferee/8_fold/meta.json index a37816d..850ac91 100644 --- a/exercises/hferee/8_fold/meta.json +++ b/exercises/hferee/8_fold/meta.json @@ -1,7 +1,12 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "Applications of the fold_right function", - "backward_exercises": ["hferee/7_lists_binary_encoding"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Applications of the fold_right function", + "focus": [ + "list manipulation" + ], + "backward_exercises": [ + "hferee/7_lists_binary_encoding" + ] } diff --git a/exercises/hferee/8_sieve/meta.json b/exercises/hferee/8_sieve/meta.json index a63800f..6f1c55f 100644 --- a/exercises/hferee/8_sieve/meta.json +++ b/exercises/hferee/8_sieve/meta.json @@ -1,7 +1,12 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "Sieve of Eratosthenes", - "backward_exercises": ["hferee/8_fold"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Sieve of Eratosthenes", + "focus": [ + "list manipulation" + ], + "backward_exercises": [ + "hferee/8_fold" + ] } diff --git a/exercises/hferee/8_sort/meta.json b/exercises/hferee/8_sort/meta.json index 42739cf..a516e12 100644 --- a/exercises/hferee/8_sort/meta.json +++ b/exercises/hferee/8_sort/meta.json @@ -1,7 +1,12 @@ { - "learnocaml_version": "1", - "kind": "exercise", - "stars": 2, - "title": "Sort by insertion", - "backward_exercises": ["hferee/8_sieve"] + "learnocaml_version": "1", + "kind": "exercise", + "stars": 2, + "title": "Sort by insertion", + "focus": [ + "list manipulation" + ], + "backward_exercises": [ + "hferee/8_sieve" + ] } diff --git a/exercises/mooc/week1/seq3/ex1/meta.json b/exercises/mooc/week1/seq3/ex1/meta.json index dddc7a1..920050f 100644 --- a/exercises/mooc/week1/seq3/ex1/meta.json +++ b/exercises/mooc/week1/seq3/ex1/meta.json @@ -1,4 +1,10 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", - "title": "Integer Identifiers", - "stars": 1 } + "title": "Integer Identifiers", + "focus": [ + " Variable declaration", + "int operators" + ], + "stars": 1 +} diff --git a/exercises/mooc/week1/seq3/ex2/meta.json b/exercises/mooc/week1/seq3/ex2/meta.json index 0ad92ef..d34666d 100644 --- a/exercises/mooc/week1/seq3/ex2/meta.json +++ b/exercises/mooc/week1/seq3/ex2/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", - "title": "String Identifiers", + "title": "String Identifiers", + "focus": [ + " Variable declaration", + "string operators" + ], "stars": 1, - "backward_exercises": ["mooc/week1/seq4/ex1"] + "backward_exercises": [ + "mooc/week1/seq4/ex1" + ] } diff --git a/exercises/mooc/week1/seq4/ex1/meta.json b/exercises/mooc/week1/seq4/ex1/meta.json index fabfc79..47d49d9 100644 --- a/exercises/mooc/week1/seq4/ex1/meta.json +++ b/exercises/mooc/week1/seq4/ex1/meta.json @@ -1,6 +1,12 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", - "title": "Simple Functions over Integers", + "title": "Simple Functions over Integers", + "focus": [ + " Numeric operators" + ], "stars": 1, - "backward_exercises" : ["hferee/1.2_declarations"] + "backward_exercises": [ + "hferee/1.2_declarations" + ] } diff --git a/exercises/mooc/week1/seq4/ex2/meta.json b/exercises/mooc/week1/seq4/ex2/meta.json index 912e3fe..9084cde 100644 --- a/exercises/mooc/week1/seq4/ex2/meta.json +++ b/exercises/mooc/week1/seq4/ex2/meta.json @@ -1,6 +1,12 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", - "title": "Simple Functions over Strings", + "title": "Simple Functions over Strings", + "focus": [ + "String functions" + ], "stars": 1, - "backward_exercises": ["mooc/week1/seq3/ex2"] + "backward_exercises": [ + "mooc/week1/seq3/ex2" + ] } diff --git a/exercises/mooc/week2/seq1/ex1/meta.json b/exercises/mooc/week2/seq1/ex1/meta.json index 87990dc..726fdb6 100644 --- a/exercises/mooc/week2/seq1/ex1/meta.json +++ b/exercises/mooc/week2/seq1/ex1/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "Tetragon", + "focus": [ + "Logical operations", + "tuple manipulations" + ], "stars": 2.5, - "backward_exercises": ["smelodesousa/F4/4-type-4"] + "backward_exercises": [ + "smelodesousa/F4/4-type-4" + ] } diff --git a/exercises/mooc/week2/seq1/ex2/meta.json b/exercises/mooc/week2/seq1/ex2/meta.json index 7bda247..ae79025 100644 --- a/exercises/mooc/week2/seq1/ex2/meta.json +++ b/exercises/mooc/week2/seq1/ex2/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "Enigma", + "focus": [ + "Arithmetic operations", + "recursive functions" + ], "stars": 2.5, - "backward_exercises": ["smelodesousa/F3/3-catalan"] + "backward_exercises": [ + "smelodesousa/F3/3-catalan" + ] } diff --git a/exercises/mooc/week2/seq2/ex1/meta.json b/exercises/mooc/week2/seq2/ex1/meta.json index c0e611c..ddba9e7 100644 --- a/exercises/mooc/week2/seq2/ex1/meta.json +++ b/exercises/mooc/week2/seq2/ex1/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", - "title": "Time on Planet Shadokus", + "title": "Time on Planet Shadokus", + "focus": [ + "Record manipulation", + "recursive functions" + ], "stars": 2, - "backward_exercises": ["hferee/4.2_coordinates"] + "backward_exercises": [ + "hferee/4.2_coordinates" + ] } diff --git a/exercises/mooc/week2/seq2/ex2/meta.json b/exercises/mooc/week2/seq2/ex2/meta.json index c3c5a7c..87143b4 100644 --- a/exercises/mooc/week2/seq2/ex2/meta.json +++ b/exercises/mooc/week2/seq2/ex2/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "Points and vectors", + "focus": [ + "Arithmetic operations", + "Record manipulation" + ], "stars": 1.5, - "backward_exercises": ["hferee/5.2_sierpinski_vg"] + "backward_exercises": [ + "hferee/5.2_sierpinski_vg" + ] } diff --git a/exercises/mooc/week2/seq3/ex1/meta.json b/exercises/mooc/week2/seq3/ex1/meta.json index 4fcbfc3..21a60e3 100644 --- a/exercises/mooc/week2/seq3/ex1/meta.json +++ b/exercises/mooc/week2/seq3/ex1/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "Searching for Strings in Arrays", + "focus": [ + "Array manipulation", + "recursive functions" + ], "stars": 2, - "backward_exercises": ["smelodesousa/F3/3-dichotomy"] + "backward_exercises": [ + "smelodesousa/F3/3-dichotomy" + ] } diff --git a/exercises/mooc/week2/seq3/ex2/meta.json b/exercises/mooc/week2/seq3/ex2/meta.json index 23b8827..42f449b 100644 --- a/exercises/mooc/week2/seq3/ex2/meta.json +++ b/exercises/mooc/week2/seq3/ex2/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "Finding the Minimum", + "focus": [ + "Array manipulation", + "recursive functions" + ], "stars": 1, - "backward_exercises": ["mooc/week2/seq3/ex1"] + "backward_exercises": [ + "mooc/week2/seq3/ex1" + ] } diff --git a/exercises/mooc/week2/seq4/ex1/meta.json b/exercises/mooc/week2/seq4/ex1/meta.json index ebca152..63ecc96 100644 --- a/exercises/mooc/week2/seq4/ex1/meta.json +++ b/exercises/mooc/week2/seq4/ex1/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "A Small Typed Database", + "focus": [ + "Record manipulation", + "recursive functions" + ], "stars": 3, - "backward_exercises": ["mooc/week2/seq2/ex1"] + "backward_exercises": [ + "mooc/week2/seq2/ex1" + ] } diff --git a/exercises/mooc/week3/seq1/ex1/meta.json b/exercises/mooc/week3/seq1/ex1/meta.json index d73e87b..6ebbdf1 100644 --- a/exercises/mooc/week3/seq1/ex1/meta.json +++ b/exercises/mooc/week3/seq1/ex1/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "First In First Out", + "focus": [ + "List manipulation", + "pattern matching" + ], "stars": 2, - "backward_exercises": ["hferee/8_sort"] + "backward_exercises": [ + "hferee/8_sort" + ] } diff --git a/exercises/mooc/week3/seq1/ex2/meta.json b/exercises/mooc/week3/seq1/ex2/meta.json index cd188c4..0e6d287 100644 --- a/exercises/mooc/week3/seq1/ex2/meta.json +++ b/exercises/mooc/week3/seq1/ex2/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "Classic Functions Over Lists", + "focus": [ + "List manipulation", + "pattern matching" + ], "stars": 2, - "backward_exercises": ["smelodesousa/F5/5-horner"] + "backward_exercises": [ + "smelodesousa/F5/5-horner" + ] } diff --git a/exercises/mooc/week3/seq2/ex1/meta.json b/exercises/mooc/week3/seq2/ex1/meta.json index 4d53677..77b2037 100644 --- a/exercises/mooc/week3/seq2/ex1/meta.json +++ b/exercises/mooc/week3/seq2/ex1/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "Symbolic Manipulation of Arithmetic Expressions", + "focus": [ + "DSL use-case", + "Variant" + ], "stars": 2, - "backward_exercises": ["smelodesousa/F5/5-randomness-is-hard"] + "backward_exercises": [ + "smelodesousa/F5/5-randomness-is-hard" + ] } diff --git a/exercises/mooc/week3/seq2/ex2/meta.json b/exercises/mooc/week3/seq2/ex2/meta.json index f80d18e..42fa6df 100644 --- a/exercises/mooc/week3/seq2/ex2/meta.json +++ b/exercises/mooc/week3/seq2/ex2/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "Tries", + "focus": [ + "Option type", + "char and string manipulations" + ], "stars": 3, - "backward_exercises": ["smelodesousa/F5/5-pizzaria"] + "backward_exercises": [ + "smelodesousa/F5/5-pizzaria" + ] } diff --git a/exercises/mooc/week3/seq3/ex1/meta.json b/exercises/mooc/week3/seq3/ex1/meta.json index f1ab797..72c933c 100644 --- a/exercises/mooc/week3/seq3/ex1/meta.json +++ b/exercises/mooc/week3/seq3/ex1/meta.json @@ -1,6 +1,13 @@ -{ "learnocaml_version": "1", +{ + "learnocaml_version": "1", "kind": "exercise", "title": "Type Directed Programming", + "focus": [ + "Code comprehension and update", + "fixing warnings" + ], "stars": 1, - "backward_exercises": ["mooc/week3/seq2/ex2"] + "backward_exercises": [ + "mooc/week3/seq2/ex2" + ] } diff --git a/exercises/smelodesousa/F1/1-errors/meta.json b/exercises/smelodesousa/F1/1-errors/meta.json index ca18826..2510582 100644 --- a/exercises/smelodesousa/F1/1-errors/meta.json +++ b/exercises/smelodesousa/F1/1-errors/meta.json @@ -1,9 +1,23 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Errors", - "identifier" : "1.5", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F1/1-future"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Errors", + "focus": [ + "Errors" + ], + "identifier": "1.5", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F1/1-future" + ] } diff --git a/exercises/smelodesousa/F1/1-fun-calls/meta.json b/exercises/smelodesousa/F1/1-fun-calls/meta.json index 5278531..d61837f 100644 --- a/exercises/smelodesousa/F1/1-fun-calls/meta.json +++ b/exercises/smelodesousa/F1/1-fun-calls/meta.json @@ -1,9 +1,24 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Function Calls", - "identifier" : "1.12", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F1/1-tuples"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Function Calls", + "focus": [ + "function call", + "types" + ], + "identifier": "1.12", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F1/1-tuples" + ] } diff --git a/exercises/smelodesousa/F1/1-future/meta.json b/exercises/smelodesousa/F1/1-future/meta.json index 08d8f70..ae0eb35 100644 --- a/exercises/smelodesousa/F1/1-future/meta.json +++ b/exercises/smelodesousa/F1/1-future/meta.json @@ -1,9 +1,33 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Future", - "identifier" : "1.4", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F1/1-mistery"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Future", + "focus": [ + "Types", + "Fonctions recursives", + "Errors" + ], + "identifier": "1.4", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F1/1-mistery" + ] } diff --git a/exercises/smelodesousa/F1/1-mistery/meta.json b/exercises/smelodesousa/F1/1-mistery/meta.json index 18b151c..62de60c 100644 --- a/exercises/smelodesousa/F1/1-mistery/meta.json +++ b/exercises/smelodesousa/F1/1-mistery/meta.json @@ -1,9 +1,28 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Mistery", - "identifier" : "1.3", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F1/1-type-error"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Mistery", + "focus": [ + "Types", + "Fonctions recursives" + ], + "identifier": "1.3", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F1/1-type-error" + ] } diff --git a/exercises/smelodesousa/F1/1-true-false/meta.json b/exercises/smelodesousa/F1/1-true-false/meta.json index ee0616d..6386742 100644 --- a/exercises/smelodesousa/F1/1-true-false/meta.json +++ b/exercises/smelodesousa/F1/1-true-false/meta.json @@ -3,10 +3,21 @@ "kind": "exercise", "stars": 1, "title": "True or false?", + "focus": [ + "Type inference" + ], "identifier": "1.8", "authors": [ - ["Rui Barata", "rui.barata@ubi.pt"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F1/1-what-type"] + "backward_exercises": [ + "smelodesousa/F1/1-what-type" + ] } diff --git a/exercises/smelodesousa/F1/1-tuples/meta.json b/exercises/smelodesousa/F1/1-tuples/meta.json index a00b4bb..058c4b6 100644 --- a/exercises/smelodesousa/F1/1-tuples/meta.json +++ b/exercises/smelodesousa/F1/1-tuples/meta.json @@ -3,10 +3,21 @@ "kind": "exercise", "stars": 1, "title": "Tuples manipulation", + "focus": [ + "tuple manipulations" + ], "identifier": "1.11", "authors": [ - ["Rui Barata", "rui.barata@ubi.pt"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F1/1-type2"] + "backward_exercises": [ + "smelodesousa/F1/1-type2" + ] } diff --git a/exercises/smelodesousa/F1/1-type-error/meta.json b/exercises/smelodesousa/F1/1-type-error/meta.json index 3c79076..f43f544 100644 --- a/exercises/smelodesousa/F1/1-type-error/meta.json +++ b/exercises/smelodesousa/F1/1-type-error/meta.json @@ -3,10 +3,21 @@ "kind": "exercise", "stars": 1, "title": "Type or error", + "focus": [ + "Type inference" + ], "identifier": "1.2", "authors": [ - ["Rui Barata", "rui.barata@ubi.pt"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F1/1-type"] + "backward_exercises": [ + "smelodesousa/F1/1-type" + ] } diff --git a/exercises/smelodesousa/F1/1-type-value/meta.json b/exercises/smelodesousa/F1/1-type-value/meta.json index eaef7aa..ce569da 100644 --- a/exercises/smelodesousa/F1/1-type-value/meta.json +++ b/exercises/smelodesousa/F1/1-type-value/meta.json @@ -1,9 +1,27 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Type and Value", - "identifier" : "1.6", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F1/1-errors"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Type and Value", + "focus": [ + "Type inference" + ], + "identifier": "1.6", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F1/1-errors" + ] } diff --git a/exercises/smelodesousa/F1/1-type/meta.json b/exercises/smelodesousa/F1/1-type/meta.json index 19e6e6a..6a02cbc 100644 --- a/exercises/smelodesousa/F1/1-type/meta.json +++ b/exercises/smelodesousa/F1/1-type/meta.json @@ -1,9 +1,27 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Types", - "identifier" : "1.1", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["hferee/1.4_conditionals"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Types", + "focus": [ + "Types" + ], + "identifier": "1.1", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "hferee/1.4_conditionals" + ] } diff --git a/exercises/smelodesousa/F1/1-type2/meta.json b/exercises/smelodesousa/F1/1-type2/meta.json index 91686d9..099e120 100644 --- a/exercises/smelodesousa/F1/1-type2/meta.json +++ b/exercises/smelodesousa/F1/1-type2/meta.json @@ -3,10 +3,21 @@ "kind": "exercise", "stars": 1, "title": "Types 2", + "focus": [ + "Types" + ], "identifier": "1.10", "authors": [ - ["Rui Barata", "rui.barata@ubi.pt"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F1/1-what-type2"] + "backward_exercises": [ + "smelodesousa/F1/1-what-type2" + ] } diff --git a/exercises/smelodesousa/F1/1-what-type/meta.json b/exercises/smelodesousa/F1/1-what-type/meta.json index 77336cd..6fe4dfc 100644 --- a/exercises/smelodesousa/F1/1-what-type/meta.json +++ b/exercises/smelodesousa/F1/1-what-type/meta.json @@ -1,9 +1,27 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "What's the Type?", - "identifier" : "1.7", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F1/1-type-value"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "What's the Type?", + "focus": [ + "Type inference" + ], + "identifier": "1.7", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F1/1-type-value" + ] } diff --git a/exercises/smelodesousa/F1/1-what-type2/meta.json b/exercises/smelodesousa/F1/1-what-type2/meta.json index e49b401..9c237ac 100644 --- a/exercises/smelodesousa/F1/1-what-type2/meta.json +++ b/exercises/smelodesousa/F1/1-what-type2/meta.json @@ -1,9 +1,23 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "What is the type? 2", - "identifier" : "1.9", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F1/1-true-false"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "What is the type? 2", + "focus": [ + "Type inference" + ], + "identifier": "1.9", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F1/1-true-false" + ] } diff --git a/exercises/smelodesousa/F3/3-a-historic-algorithm/meta.json b/exercises/smelodesousa/F3/3-a-historic-algorithm/meta.json index a88a287..6ccbb36 100644 --- a/exercises/smelodesousa/F3/3-a-historic-algorithm/meta.json +++ b/exercises/smelodesousa/F3/3-a-historic-algorithm/meta.json @@ -3,10 +3,23 @@ "kind": "exercise", "stars": 1, "title": "A historic algorithm", + "focus": [ + "Arithmetic operations", + "recursive functions", + "exceptions" + ], "identifier": "3.3", "authors": [ - ["Dário Santos", "dariovfsantos@gmail.com"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F3/3-manhattan-distance"] + "backward_exercises": [ + "smelodesousa/F3/3-manhattan-distance" + ] } diff --git a/exercises/smelodesousa/F3/3-catalan/meta.json b/exercises/smelodesousa/F3/3-catalan/meta.json index 36e4d72..6177867 100644 --- a/exercises/smelodesousa/F3/3-catalan/meta.json +++ b/exercises/smelodesousa/F3/3-catalan/meta.json @@ -1,9 +1,29 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Catalan: a language, a desert and some numbers", - "identifier" : "3.13", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Melo de Sousa",""], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F3/3-fast-exponentiation"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Catalan: a language, a desert and some numbers", + "focus": [ + "Arithmetic operations", + "recursive functions", + "exceptions" + ], + "identifier": "3.13", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Melo de Sousa", + "" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-fast-exponentiation" + ] } diff --git a/exercises/smelodesousa/F3/3-collatz-hailstones/meta.json b/exercises/smelodesousa/F3/3-collatz-hailstones/meta.json index 17858d2..8340fb8 100644 --- a/exercises/smelodesousa/F3/3-collatz-hailstones/meta.json +++ b/exercises/smelodesousa/F3/3-collatz-hailstones/meta.json @@ -1,9 +1,32 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "The Collatz conjecture, or Hailstones, or Syracuse and other cool names...", - "identifier" : "3.17", - "authors" : [["Leonardo Santos", "leomendesantos@gmail.com"], ["Dário Santos","dariovfsantos@gmail.com"], ["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F3/3-triangles"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "The Collatz conjecture, or Hailstones, or Syracuse and other cool names...", + "focus": [ + "Arithmetic operations", + "recursive functions" + ], + "identifier": "3.17", + "authors": [ + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ], + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-triangles" + ] } diff --git a/exercises/smelodesousa/F3/3-dichotomy/meta.json b/exercises/smelodesousa/F3/3-dichotomy/meta.json index ebc3837..81e56c7 100644 --- a/exercises/smelodesousa/F3/3-dichotomy/meta.json +++ b/exercises/smelodesousa/F3/3-dichotomy/meta.json @@ -3,11 +3,25 @@ "kind": "exercise", "stars": 2, "title": "Dichotomy", + "focus": [ + "Array manipulation" + ], "identifier": "3.14", "authors": [ - ["Dário Santos", "dariovfsantos@gmail.com"], - ["Gonçalo Domingos", "goncalogdomingos@gmail.com"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F3/3-how-many-mountains"] + "backward_exercises": [ + "smelodesousa/F3/3-how-many-mountains" + ] } diff --git a/exercises/smelodesousa/F3/3-digits/meta.json b/exercises/smelodesousa/F3/3-digits/meta.json index ee4f050..73ab02f 100644 --- a/exercises/smelodesousa/F3/3-digits/meta.json +++ b/exercises/smelodesousa/F3/3-digits/meta.json @@ -1,9 +1,24 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Digits", - "identifier" : "3.6", - "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F3/3-pi"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Digits", + "focus": [ + "Arithmetic operations", + "recursive functions" + ], + "identifier": "3.6", + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-pi" + ] } diff --git a/exercises/smelodesousa/F3/3-dragon-greatness/meta.json b/exercises/smelodesousa/F3/3-dragon-greatness/meta.json index cde3f80..da8306b 100644 --- a/exercises/smelodesousa/F3/3-dragon-greatness/meta.json +++ b/exercises/smelodesousa/F3/3-dragon-greatness/meta.json @@ -3,10 +3,22 @@ "kind": "exercise", "stars": 3, "title": "The dragon's greatness", + "focus": [ + "Array manipulation", + "list manipulation" + ], "identifier": "3.21", "authors": [ - ["Dário Santos", "dariovfsantos@gmail.com"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["hferee/10_assoc"] + "backward_exercises": [ + "hferee/10_assoc" + ] } diff --git a/exercises/smelodesousa/F3/3-fast-exponentiation/meta.json b/exercises/smelodesousa/F3/3-fast-exponentiation/meta.json index 9d2e354..0c8eb2a 100644 --- a/exercises/smelodesousa/F3/3-fast-exponentiation/meta.json +++ b/exercises/smelodesousa/F3/3-fast-exponentiation/meta.json @@ -1,9 +1,32 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Fast Exponentiation", - "identifier" : "3.12", - "authors" : [["Leonardo Santos", "leomendesantos@gmail.com"], ["Dário Santos","dariovfsantos@gmail.com"], ["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F3/3-tribonacci"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Fast Exponentiation", + "focus": [ + "Arithmetic operations", + "recursive functions" + ], + "identifier": "3.12", + "authors": [ + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ], + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-tribonacci" + ] } diff --git a/exercises/smelodesousa/F3/3-hofstadter/meta.json b/exercises/smelodesousa/F3/3-hofstadter/meta.json index 79d9b83..1d0f327 100644 --- a/exercises/smelodesousa/F3/3-hofstadter/meta.json +++ b/exercises/smelodesousa/F3/3-hofstadter/meta.json @@ -1,9 +1,28 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Hofstadter's female and male number sequences", - "identifier" : "3.8", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Melo de Sousa",""], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F3/3-seq-hofstadter"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Hofstadter's female and male number sequences", + "focus": [ + "Arithmetic operations", + "exceptions" + ], + "identifier": "3.8", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Melo de Sousa", + "" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-seq-hofstadter" + ] } diff --git a/exercises/smelodesousa/F3/3-how-many-mountains/meta.json b/exercises/smelodesousa/F3/3-how-many-mountains/meta.json index 6aaf28f..1d43ed8 100644 --- a/exercises/smelodesousa/F3/3-how-many-mountains/meta.json +++ b/exercises/smelodesousa/F3/3-how-many-mountains/meta.json @@ -3,11 +3,25 @@ "kind": "exercise", "stars": 3, "title": "How many mountains?", + "focus": [ + "Arithmetic operations" + ], "identifier": "3.20", "authors": [ - ["Dário Santos", "dariovfsantos@gmail.com"], - ["Gonçalo Domingos", "goncalogdomingos@gmail.com"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F3/3-collatz-hailstones"] + "backward_exercises": [ + "smelodesousa/F3/3-collatz-hailstones" + ] } diff --git a/exercises/smelodesousa/F3/3-manhattan-distance/meta.json b/exercises/smelodesousa/F3/3-manhattan-distance/meta.json index 48d3c55..3b50411 100644 --- a/exercises/smelodesousa/F3/3-manhattan-distance/meta.json +++ b/exercises/smelodesousa/F3/3-manhattan-distance/meta.json @@ -1,9 +1,23 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Manhattan distance", - "identifier" : "3.2", - "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F3/3-simple-math"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Manhattan distance", + "focus": [ + " Arithmetic operations" + ], + "identifier": "3.2", + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-simple-math" + ] } diff --git a/exercises/smelodesousa/F3/3-mccarthy-91-function/meta.json b/exercises/smelodesousa/F3/3-mccarthy-91-function/meta.json index 62e285d..c0440ad 100644 --- a/exercises/smelodesousa/F3/3-mccarthy-91-function/meta.json +++ b/exercises/smelodesousa/F3/3-mccarthy-91-function/meta.json @@ -1,9 +1,28 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "McCarthy 91 function", - "identifier" : "3.15", - "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["mooc/week2/seq2/ex2"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "McCarthy 91 function", + "focus": [ + "Arithmetic operations", + "recursive functions" + ], + "identifier": "3.15", + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "mooc/week2/seq2/ex2" + ] } diff --git a/exercises/smelodesousa/F3/3-pi/meta.json b/exercises/smelodesousa/F3/3-pi/meta.json index af7e6fb..229b517 100644 --- a/exercises/smelodesousa/F3/3-pi/meta.json +++ b/exercises/smelodesousa/F3/3-pi/meta.json @@ -3,10 +3,23 @@ "kind": "exercise", "stars": 1, "title": "Approximate π", + "focus": [ + "Arithmetic operations", + "recursive functions", + "floats" + ], "identifier": "3.5", "authors": [ - ["Rui Barata", "rui.barata@ubi.pt"], - ["Simão Melo de Sousa", ""] + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Melo de Sousa", + "" + ] ], - "backward_exercises": ["smelodesousa/F3/3-reach-infinity"] + "backward_exercises": [ + "smelodesousa/F3/3-reach-infinity" + ] } diff --git a/exercises/smelodesousa/F3/3-reach-infinity/meta.json b/exercises/smelodesousa/F3/3-reach-infinity/meta.json index dbb029a..ac91df7 100644 --- a/exercises/smelodesousa/F3/3-reach-infinity/meta.json +++ b/exercises/smelodesousa/F3/3-reach-infinity/meta.json @@ -1,9 +1,28 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Reach infinity", - "identifier" : "3.4", - "authors" : [["Dário Santos","dariovfsantos@gmail.com"], ["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F3/3-a-historic-algorithm"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Reach infinity", + "focus": [ + "Arithmetic operations", + "recursive functions" + ], + "identifier": "3.4", + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-a-historic-algorithm" + ] } diff --git a/exercises/smelodesousa/F3/3-seq-hofstadter/meta.json b/exercises/smelodesousa/F3/3-seq-hofstadter/meta.json index 470be1b..8466386 100644 --- a/exercises/smelodesousa/F3/3-seq-hofstadter/meta.json +++ b/exercises/smelodesousa/F3/3-seq-hofstadter/meta.json @@ -3,7 +3,19 @@ "kind": "exercise", "stars": 1, "title": "The Hofstadter-Huber Qr,s(n) sequences", + "focus": [ + "Arithmetic operations", + "recursive functions", + "exceptions" + ], "identifier": "3.7", - "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], - "backward_exercises": ["smelodesousa/F3/3-digits"] + "authors": [ + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-digits" + ] } diff --git a/exercises/smelodesousa/F3/3-simple-math/meta.json b/exercises/smelodesousa/F3/3-simple-math/meta.json index 5c00586..db41a6d 100644 --- a/exercises/smelodesousa/F3/3-simple-math/meta.json +++ b/exercises/smelodesousa/F3/3-simple-math/meta.json @@ -3,10 +3,22 @@ "kind": "exercise", "stars": 1, "title": "Simple math", + "focus": [ + "Arithmetic operations", + "floats" + ], "identifier": "3.1", "authors": [ - ["Rui Barata", "rui.barata@ubi.pt"], - ["Simão Melo de Sousa", ""] + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Melo de Sousa", + "" + ] ], - "backward_exercises": ["mooc/week2/seq1/ex1"] + "backward_exercises": [ + "mooc/week2/seq1/ex1" + ] } diff --git a/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/meta.json b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/meta.json index 9145124..baed920 100644 --- a/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/meta.json +++ b/exercises/smelodesousa/F3/3-stars-in-the-form-of-textual-fractals/meta.json @@ -3,11 +3,25 @@ "kind": "exercise", "stars": 2, "title": "Stars in the form of textual fractals", + "focus": [ + "Print functions" + ], "identifier": "3.18", "authors": [ - ["Dário Santos", "dariovfsantos@gmail.com"], - ["Rui Barata", "rui.barata@ubi.pt"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["hferee/6_sierpinsky_ascii"] + "backward_exercises": [ + "hferee/6_sierpinsky_ascii" + ] } diff --git a/exercises/smelodesousa/F3/3-sums/meta.json b/exercises/smelodesousa/F3/3-sums/meta.json index 8c6f4ff..58a2f03 100644 --- a/exercises/smelodesousa/F3/3-sums/meta.json +++ b/exercises/smelodesousa/F3/3-sums/meta.json @@ -1,9 +1,24 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Sums", - "identifier" : "3.9", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"],["Simão Melo de Sousa",""]], - "backward_exercises": ["smelodesousa/F3/3-hofstadter"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Sums", + "focus": [ + "Arithmetic operations", + "recursive functions" + ], + "identifier": "3.9", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Melo de Sousa", + "" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-hofstadter" + ] } diff --git a/exercises/smelodesousa/F3/3-triangles/meta.json b/exercises/smelodesousa/F3/3-triangles/meta.json index 4d96ddf..16116dc 100644 --- a/exercises/smelodesousa/F3/3-triangles/meta.json +++ b/exercises/smelodesousa/F3/3-triangles/meta.json @@ -1,9 +1,24 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Triangles", - "identifier" : "3.16", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"],["Simão Melo de Sousa",""]], - "backward_exercises": ["smelodesousa/F3/3-mccarthy-91-function"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Triangles", + "focus": [ + "Arithmetic operations", + "recursive functions" + ], + "identifier": "3.16", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Melo de Sousa", + "" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-mccarthy-91-function" + ] } diff --git a/exercises/smelodesousa/F3/3-tribonacci/meta.json b/exercises/smelodesousa/F3/3-tribonacci/meta.json index d2d3a3e..f95353b 100644 --- a/exercises/smelodesousa/F3/3-tribonacci/meta.json +++ b/exercises/smelodesousa/F3/3-tribonacci/meta.json @@ -3,10 +3,23 @@ "kind": "exercise", "stars": 2, "title": "Tribonacci", + "focus": [ + "Arithmetic operations", + "recursive functions", + "exceptions" + ], "identifier": "3.10", "authors": [ - ["Dário Santos", "dariovfsantos@gmail.com"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F3/3-sums"] + "backward_exercises": [ + "smelodesousa/F3/3-sums" + ] } diff --git a/exercises/smelodesousa/F4/4-fun-1/meta.json b/exercises/smelodesousa/F4/4-fun-1/meta.json index d8ae541..fbbd56c 100644 --- a/exercises/smelodesousa/F4/4-fun-1/meta.json +++ b/exercises/smelodesousa/F4/4-fun-1/meta.json @@ -1,9 +1,27 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Functions I", - "identifier" : "4.7", - "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F4/4-type-1"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Functions I", + "focus": [ + "polymorphic types" + ], + "identifier": "4.7", + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F4/4-type-1" + ] } diff --git a/exercises/smelodesousa/F4/4-fun-2/meta.json b/exercises/smelodesousa/F4/4-fun-2/meta.json index 81538fa..357e02d 100644 --- a/exercises/smelodesousa/F4/4-fun-2/meta.json +++ b/exercises/smelodesousa/F4/4-fun-2/meta.json @@ -1,9 +1,27 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Functions II", - "identifier" : "4.7", - "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"]], - "backward_exercises": ["smelodesousa/F4/4-fun-1"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Functions II", + "focus": [ + "polymorphic types" + ], + "identifier": "4.7", + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F4/4-fun-1" + ] } diff --git a/exercises/smelodesousa/F4/4-type-1/meta.json b/exercises/smelodesousa/F4/4-type-1/meta.json index 990f661..4efb12f 100644 --- a/exercises/smelodesousa/F4/4-type-1/meta.json +++ b/exercises/smelodesousa/F4/4-type-1/meta.json @@ -1,9 +1,31 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Verify the type I", - "identifier" : "4.1", - "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F3/3-stars-in-the-form-of-textual-fractals"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Verify the type I", + "focus": [ + "Polymorphic types" + ], + "identifier": "4.1", + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F3/3-stars-in-the-form-of-textual-fractals" + ] } diff --git a/exercises/smelodesousa/F4/4-type-2/meta.json b/exercises/smelodesousa/F4/4-type-2/meta.json index 59148f3..e62331d 100644 --- a/exercises/smelodesousa/F4/4-type-2/meta.json +++ b/exercises/smelodesousa/F4/4-type-2/meta.json @@ -1,9 +1,27 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Verify the type II", - "identifier" : "4.2", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Simão Sousa", "desousa@di.ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F1/1-fun-calls"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Verify the type II", + "focus": [ + "types" + ], + "identifier": "4.2", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F1/1-fun-calls" + ] } diff --git a/exercises/smelodesousa/F4/4-type-3/meta.json b/exercises/smelodesousa/F4/4-type-3/meta.json index 663b929..ed8ee40 100644 --- a/exercises/smelodesousa/F4/4-type-3/meta.json +++ b/exercises/smelodesousa/F4/4-type-3/meta.json @@ -3,10 +3,21 @@ "kind": "exercise", "stars": 1, "title": "Check type III", + "focus": [ + "types" + ], "identifier": "4.3", "authors": [ - ["Rui Barata", "rui.barata@ubi.pt"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F4/4-type-2"] + "backward_exercises": [ + "smelodesousa/F4/4-type-2" + ] } diff --git a/exercises/smelodesousa/F4/4-type-4/meta.json b/exercises/smelodesousa/F4/4-type-4/meta.json index 91b8c08..dd4f7c4 100644 --- a/exercises/smelodesousa/F4/4-type-4/meta.json +++ b/exercises/smelodesousa/F4/4-type-4/meta.json @@ -3,10 +3,21 @@ "kind": "exercise", "stars": 1, "title": "Check type IV", + "focus": [ + "types" + ], "identifier": "4.4", "authors": [ - ["Rui Barata", "rui.barata@ubi.pt"], - ["Simão Sousa", "desousa@di.ubi.pt"] + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Simão Sousa", + "desousa@di.ubi.pt" + ] ], - "backward_exercises": ["smelodesousa/F4/4-type-3"] + "backward_exercises": [ + "smelodesousa/F4/4-type-3" + ] } diff --git a/exercises/smelodesousa/F5/5-absolute-majority/meta.json b/exercises/smelodesousa/F5/5-absolute-majority/meta.json index b0fc3bc..2023ca3 100644 --- a/exercises/smelodesousa/F5/5-absolute-majority/meta.json +++ b/exercises/smelodesousa/F5/5-absolute-majority/meta.json @@ -1,9 +1,24 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Absolute majority!", - "identifier" : "5.12", - "authors" : [["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-zombie-attack"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Absolute majority!", + "focus": [ + "Array manipulation", + "list manipulation" + ], + "identifier": "5.12", + "authors": [ + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-zombie-attack" + ] } diff --git a/exercises/smelodesousa/F5/5-brackets/meta.json b/exercises/smelodesousa/F5/5-brackets/meta.json index c5850a0..d24a7bb 100644 --- a/exercises/smelodesousa/F5/5-brackets/meta.json +++ b/exercises/smelodesousa/F5/5-brackets/meta.json @@ -1,9 +1,19 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 3, - "title" : "Correctly using parentheses", - "identifier" : "5.20", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], - "backward_exercises": ["smelodesousa/F5/5-subsequence-of-lists"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 3, + "title": "Correctly using parentheses", + "focus": [ + "List manipulation" + ], + "identifier": "5.20", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-subsequence-of-lists" + ] } diff --git a/exercises/smelodesousa/F5/5-burrows-wheeler/meta.json b/exercises/smelodesousa/F5/5-burrows-wheeler/meta.json index f41a488..d987092 100644 --- a/exercises/smelodesousa/F5/5-burrows-wheeler/meta.json +++ b/exercises/smelodesousa/F5/5-burrows-wheeler/meta.json @@ -3,7 +3,17 @@ "kind": "exercise", "stars": 3, "title": "Burrows-Wheeler transform", + "focus": [ + "String manipulation" + ], "identifier": "5.18", - "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], - "backward_exercises": ["mooc/week3/seq3/ex1"] + "authors": [ + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ] + ], + "backward_exercises": [ + "mooc/week3/seq3/ex1" + ] } diff --git a/exercises/smelodesousa/F5/5-gray-codes/meta.json b/exercises/smelodesousa/F5/5-gray-codes/meta.json index 2abfa44..0d5f5da 100644 --- a/exercises/smelodesousa/F5/5-gray-codes/meta.json +++ b/exercises/smelodesousa/F5/5-gray-codes/meta.json @@ -1,9 +1,24 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Gray codes", - "identifier" : "5.4", - "authors" : [["Gonçalo Domingos","goncalogdomingos@gmail.com"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-holand"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Gray codes", + "focus": [ + "List manipulation", + "string manipulation" + ], + "identifier": "5.4", + "authors": [ + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-holand" + ] } diff --git a/exercises/smelodesousa/F5/5-half/meta.json b/exercises/smelodesousa/F5/5-half/meta.json index aa37084..1502140 100644 --- a/exercises/smelodesousa/F5/5-half/meta.json +++ b/exercises/smelodesousa/F5/5-half/meta.json @@ -1,9 +1,23 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "In case of litigation, split it in half", - "identifier" : "5.5", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-salc"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "In case of litigation, split it in half", + "focus": [ + "List manipulation" + ], + "identifier": "5.5", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-salc" + ] } diff --git a/exercises/smelodesousa/F5/5-holand/meta.json b/exercises/smelodesousa/F5/5-holand/meta.json index bf648f0..7c0315c 100644 --- a/exercises/smelodesousa/F5/5-holand/meta.json +++ b/exercises/smelodesousa/F5/5-holand/meta.json @@ -3,7 +3,18 @@ "kind": "exercise", "stars": 2, "title": "The dutch flag problem", + "focus": [ + "Array manipulation", + "Variant types" + ], "identifier": "5.16", - "authors": [["Rui Barata", "rui.barata@ubi.pt"]], - "backward_exercises": ["smelodesousa/F5/5-absolute-majority"] + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-absolute-majority" + ] } diff --git a/exercises/smelodesousa/F5/5-horner/meta.json b/exercises/smelodesousa/F5/5-horner/meta.json index d5cde7d..d81d475 100644 --- a/exercises/smelodesousa/F5/5-horner/meta.json +++ b/exercises/smelodesousa/F5/5-horner/meta.json @@ -3,7 +3,17 @@ "kind": "exercise", "stars": 2, "title": "Operations on single variable Polynomials - Horner's method and derivation", + "focus": [ + "List manipulation" + ], "identifier": "5.6", - "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-half"] + "authors": [ + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-half" + ] } diff --git a/exercises/smelodesousa/F5/5-lists-sublists-1/meta.json b/exercises/smelodesousa/F5/5-lists-sublists-1/meta.json index 5f8cbbe..bcc1d21 100644 --- a/exercises/smelodesousa/F5/5-lists-sublists-1/meta.json +++ b/exercises/smelodesousa/F5/5-lists-sublists-1/meta.json @@ -1,9 +1,19 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Lists and sublists I", - "identifier" : "5.9", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], - "backward_exercises": ["smelodesousa/F5/5-seq-true"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Lists and sublists I", + "focus": [ + "List manipulation" + ], + "identifier": "5.9", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-seq-true" + ] } diff --git a/exercises/smelodesousa/F5/5-lists-sublists-2/meta.json b/exercises/smelodesousa/F5/5-lists-sublists-2/meta.json index 0628a48..b741dbb 100644 --- a/exercises/smelodesousa/F5/5-lists-sublists-2/meta.json +++ b/exercises/smelodesousa/F5/5-lists-sublists-2/meta.json @@ -1,9 +1,19 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Lists and sublists II", - "identifier" : "5.10", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], - "backward_exercises": ["smelodesousa/F5/5-lists-sublists-1"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Lists and sublists II", + "focus": [ + "List manipulation" + ], + "identifier": "5.10", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-lists-sublists-1" + ] } diff --git a/exercises/smelodesousa/F5/5-lists-sublists-3/meta.json b/exercises/smelodesousa/F5/5-lists-sublists-3/meta.json index 40c209c..18bd0d2 100644 --- a/exercises/smelodesousa/F5/5-lists-sublists-3/meta.json +++ b/exercises/smelodesousa/F5/5-lists-sublists-3/meta.json @@ -1,9 +1,19 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Lists and sub-lists III", - "identifier" : "5.11", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], - "backward_exercises": ["smelodesousa/F5/5-lists-sublists-2"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Lists and sub-lists III", + "focus": [ + "List manipulation" + ], + "identifier": "5.11", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-lists-sublists-2" + ] } diff --git a/exercises/smelodesousa/F5/5-lists-sublists-4/meta.json b/exercises/smelodesousa/F5/5-lists-sublists-4/meta.json index b4d3a82..6fe06d6 100644 --- a/exercises/smelodesousa/F5/5-lists-sublists-4/meta.json +++ b/exercises/smelodesousa/F5/5-lists-sublists-4/meta.json @@ -1,9 +1,23 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Lists and sub-lists IV", - "identifier" : "5.12", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-lists-sublists-3"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Lists and sub-lists IV", + "focus": [ + "List manipulation" + ], + "identifier": "5.12", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-lists-sublists-3" + ] } diff --git a/exercises/smelodesousa/F5/5-lists/meta.json b/exercises/smelodesousa/F5/5-lists/meta.json index 3b6ef69..68cab30 100644 --- a/exercises/smelodesousa/F5/5-lists/meta.json +++ b/exercises/smelodesousa/F5/5-lists/meta.json @@ -1,9 +1,19 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Utilities about Lists", - "identifier" : "5.1", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"]], - "backward_exercises": ["mooc/week3/seq1/ex1"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Utilities about Lists", + "focus": [ + "List manipulation" + ], + "identifier": "5.1", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ] + ], + "backward_exercises": [ + "mooc/week3/seq1/ex1" + ] } diff --git a/exercises/smelodesousa/F5/5-lotto/meta.json b/exercises/smelodesousa/F5/5-lotto/meta.json index 692ff60..b9e64e4 100644 --- a/exercises/smelodesousa/F5/5-lotto/meta.json +++ b/exercises/smelodesousa/F5/5-lotto/meta.json @@ -1,9 +1,24 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Lotto", - "identifier" : "5.3", - "authors" : [["Dário Santos", "dariovfsantos@gmail.com"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-shine-in-society"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Lotto", + "focus": [ + "List manipulation", + "types" + ], + "identifier": "5.3", + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-shine-in-society" + ] } diff --git a/exercises/smelodesousa/F5/5-max-sub-list/meta.json b/exercises/smelodesousa/F5/5-max-sub-list/meta.json index fe28162..214cd6e 100644 --- a/exercises/smelodesousa/F5/5-max-sub-list/meta.json +++ b/exercises/smelodesousa/F5/5-max-sub-list/meta.json @@ -3,7 +3,17 @@ "kind": "exercise", "stars": 2, "title": "The maximum sublist problem", + "focus": [ + "List manipulation" + ], "identifier": "5.15", - "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-gray-codes"] + "authors": [ + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-gray-codes" + ] } diff --git a/exercises/smelodesousa/F5/5-pizzaria/meta.json b/exercises/smelodesousa/F5/5-pizzaria/meta.json index 8c1db99..0b2b1ec 100644 --- a/exercises/smelodesousa/F5/5-pizzaria/meta.json +++ b/exercises/smelodesousa/F5/5-pizzaria/meta.json @@ -3,7 +3,17 @@ "kind": "exercise", "stars": 3, "title": "Luigi’s Pizzeria - From MIUP 2018", + "focus": [ + "List manipulation" + ], "identifier": "5.23", - "authors": [["Rui Barata", "rui.barata@ubi.pt"]], - "backward_exercises": ["smelodesousa/F5/5-brackets"] + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-brackets" + ] } diff --git a/exercises/smelodesousa/F5/5-randomness-is-hard/meta.json b/exercises/smelodesousa/F5/5-randomness-is-hard/meta.json index b3fdb67..b4c6efa 100644 --- a/exercises/smelodesousa/F5/5-randomness-is-hard/meta.json +++ b/exercises/smelodesousa/F5/5-randomness-is-hard/meta.json @@ -3,7 +3,17 @@ "kind": "exercise", "stars": 2, "title": "Randomness is hard", + "focus": [ + "Array manipulation" + ], "identifier": "5.10", - "authors": [["Dário Santos", "dariovfsantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-lists-sublists-4"] + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-lists-sublists-4" + ] } diff --git a/exercises/smelodesousa/F5/5-rle/meta.json b/exercises/smelodesousa/F5/5-rle/meta.json index 8d2c05e..d2f19f6 100644 --- a/exercises/smelodesousa/F5/5-rle/meta.json +++ b/exercises/smelodesousa/F5/5-rle/meta.json @@ -1,9 +1,24 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 2, - "title" : "Run-length encoder", - "identifier" : "5.7", - "authors" : [["Rui Barata", "rui.barata@ubi.pt"], ["Leonardo Santos", "leomendesantos@gmail.com"]], - "backward_exercises": ["mooc/week3/seq1/ex2"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 2, + "title": "Run-length encoder", + "focus": [ + "List manipulation", + "types" + ], + "identifier": "5.7", + "authors": [ + [ + "Rui Barata", + "rui.barata@ubi.pt" + ], + [ + "Leonardo Santos", + "leomendesantos@gmail.com" + ] + ], + "backward_exercises": [ + "mooc/week3/seq1/ex2" + ] } diff --git a/exercises/smelodesousa/F5/5-salc/meta.json b/exercises/smelodesousa/F5/5-salc/meta.json index 1da19e6..376bb8a 100644 --- a/exercises/smelodesousa/F5/5-salc/meta.json +++ b/exercises/smelodesousa/F5/5-salc/meta.json @@ -3,7 +3,18 @@ "kind": "exercise", "stars": 1, "title": "Sorting arrays and lists according to certain criteria", + "focus": [ + "Array manipulation", + "list manipulation" + ], "identifier": "5.4", - "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-lotto"] + "authors": [ + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-lotto" + ] } diff --git a/exercises/smelodesousa/F5/5-seq-true/meta.json b/exercises/smelodesousa/F5/5-seq-true/meta.json index 5538608..db0ebed 100644 --- a/exercises/smelodesousa/F5/5-seq-true/meta.json +++ b/exercises/smelodesousa/F5/5-seq-true/meta.json @@ -3,7 +3,17 @@ "kind": "exercise", "stars": 2, "title": "Maximum sequence of true", + "focus": [ + "List manipulation" + ], "identifier": "5.8", - "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-rle"] + "authors": [ + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-rle" + ] } diff --git a/exercises/smelodesousa/F5/5-shine-in-society/meta.json b/exercises/smelodesousa/F5/5-shine-in-society/meta.json index 8fe2f49..4e360d6 100644 --- a/exercises/smelodesousa/F5/5-shine-in-society/meta.json +++ b/exercises/smelodesousa/F5/5-shine-in-society/meta.json @@ -1,9 +1,20 @@ { - "learnocaml_version" : "2", - "kind" : "exercise", - "stars" : 1, - "title" : "Shine in society", - "identifier" : "5.2", - "authors" : [["Dário Santos", "dariovfsantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-lists"] + "learnocaml_version": "2", + "kind": "exercise", + "stars": 1, + "title": "Shine in society", + "focus": [ + "List manipulation", + "String manipulation" + ], + "identifier": "5.2", + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-lists" + ] } diff --git a/exercises/smelodesousa/F5/5-subsequence-of-lists/meta.json b/exercises/smelodesousa/F5/5-subsequence-of-lists/meta.json index 7e811d2..a512cdf 100644 --- a/exercises/smelodesousa/F5/5-subsequence-of-lists/meta.json +++ b/exercises/smelodesousa/F5/5-subsequence-of-lists/meta.json @@ -3,7 +3,17 @@ "kind": "exercise", "stars": 3, "title": "Subsequence of lists", + "focus": [ + "List manipulation" + ], "identifier": "5.5", - "authors": [["Dário Santos", "dariovfsantos@gmail.com"]], - "backward_exercises": ["smelodesousa/F5/5-max-sub-list"] + "authors": [ + [ + "Dário Santos", + "dariovfsantos@gmail.com" + ] + ], + "backward_exercises": [ + "smelodesousa/F5/5-max-sub-list" + ] } diff --git a/exercises/smelodesousa/F5/5-zombie-attack/meta.json b/exercises/smelodesousa/F5/5-zombie-attack/meta.json index a36438e..529d24c 100644 --- a/exercises/smelodesousa/F5/5-zombie-attack/meta.json +++ b/exercises/smelodesousa/F5/5-zombie-attack/meta.json @@ -3,7 +3,18 @@ "kind": "exercise", "stars": 2, "title": "Zombie attack!", + "focus": [ + "Array manipulation", + "char manipulation" + ], "identifier": "5.11", - "authors": [["Gonçalo Domingos", "goncalogdomingos@gmail.com"]], - "backward_exercises": ["hferee/11_printable"] + "authors": [ + [ + "Gonçalo Domingos", + "goncalogdomingos@gmail.com" + ] + ], + "backward_exercises": [ + "hferee/11_printable" + ] } From 32d1ae5aad6c92b27a7663ab64fd9920843a4ace Mon Sep 17 00:00:00 2001 From: RadioPotin Date: Wed, 7 Jun 2023 14:57:10 +0200 Subject: [PATCH 5/8] remove undesired whitespaces in focus points --- exercises/mooc/week1/seq3/ex1/meta.json | 2 +- exercises/mooc/week1/seq3/ex2/meta.json | 2 +- exercises/mooc/week1/seq4/ex1/meta.json | 2 +- exercises/smelodesousa/F3/3-manhattan-distance/meta.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/mooc/week1/seq3/ex1/meta.json b/exercises/mooc/week1/seq3/ex1/meta.json index 920050f..672776d 100644 --- a/exercises/mooc/week1/seq3/ex1/meta.json +++ b/exercises/mooc/week1/seq3/ex1/meta.json @@ -3,7 +3,7 @@ "kind": "exercise", "title": "Integer Identifiers", "focus": [ - " Variable declaration", + "Variable declaration", "int operators" ], "stars": 1 diff --git a/exercises/mooc/week1/seq3/ex2/meta.json b/exercises/mooc/week1/seq3/ex2/meta.json index d34666d..9b1a5d9 100644 --- a/exercises/mooc/week1/seq3/ex2/meta.json +++ b/exercises/mooc/week1/seq3/ex2/meta.json @@ -3,7 +3,7 @@ "kind": "exercise", "title": "String Identifiers", "focus": [ - " Variable declaration", + "Variable declaration", "string operators" ], "stars": 1, diff --git a/exercises/mooc/week1/seq4/ex1/meta.json b/exercises/mooc/week1/seq4/ex1/meta.json index 47d49d9..3ff75bc 100644 --- a/exercises/mooc/week1/seq4/ex1/meta.json +++ b/exercises/mooc/week1/seq4/ex1/meta.json @@ -3,7 +3,7 @@ "kind": "exercise", "title": "Simple Functions over Integers", "focus": [ - " Numeric operators" + "Numeric operators" ], "stars": 1, "backward_exercises": [ diff --git a/exercises/smelodesousa/F3/3-manhattan-distance/meta.json b/exercises/smelodesousa/F3/3-manhattan-distance/meta.json index 3b50411..a90d511 100644 --- a/exercises/smelodesousa/F3/3-manhattan-distance/meta.json +++ b/exercises/smelodesousa/F3/3-manhattan-distance/meta.json @@ -4,7 +4,7 @@ "stars": 1, "title": "Manhattan distance", "focus": [ - " Arithmetic operations" + "Arithmetic operations" ], "identifier": "3.2", "authors": [ From 2cb30afc10dff0292e12308c200521dca1537a34 Mon Sep 17 00:00:00 2001 From: Mohamed Hernouf Date: Mon, 24 Jul 2023 17:02:07 +0200 Subject: [PATCH 6/8] Remaining traduction --- exercises/hferee/1.2_declarations/template.ml | 2 +- exercises/hferee/1.2_declarations/test.ml | 4 +- exercises/hferee/1.3_bool/descr.md | 20 ++-- exercises/hferee/1.3_bool/solution.ml | 20 ++-- exercises/hferee/1.3_bool/template.ml | 20 ++-- exercises/hferee/1.3_bool/test.ml | 40 +++---- exercises/hferee/1.4_conditionals/descr.md | 6 +- exercises/hferee/1.4_conditionals/prepare.ml | 2 +- exercises/hferee/1.4_conditionals/solution.ml | 8 +- exercises/hferee/1.4_conditionals/template.ml | 12 +-- exercises/hferee/1.4_conditionals/test.ml | 10 +- exercises/hferee/11_printable/test.ml | 2 +- exercises/hferee/3.0_sudoku/descr.md | 56 +++++----- exercises/hferee/3.0_sudoku/prelude.ml | 26 ++--- exercises/hferee/3.0_sudoku/solution.ml | 94 ++++++++-------- exercises/hferee/3.0_sudoku/template.ml | 4 +- exercises/hferee/3.0_sudoku/test.ml | 100 +++++++++--------- exercises/hferee/4.2_coordinates/descr.md | 4 +- exercises/hferee/4.2_coordinates/solution.ml | 4 +- exercises/hferee/6_hanoi/solution.ml | 6 +- exercises/hferee/8_sort/test.ml | 2 +- 21 files changed, 221 insertions(+), 221 deletions(-) diff --git a/exercises/hferee/1.2_declarations/template.ml b/exercises/hferee/1.2_declarations/template.ml index 75e02e2..19f4b80 100644 --- a/exercises/hferee/1.2_declarations/template.ml +++ b/exercises/hferee/1.2_declarations/template.ml @@ -1,4 +1,4 @@ -(* Remplacer les -1 par vos réponses *) +(* Replace the -1 with your answers *) let phrase0 = -1 diff --git a/exercises/hferee/1.2_declarations/test.ml b/exercises/hferee/1.2_declarations/test.ml index 27b4fa6..126733d 100644 --- a/exercises/hferee/1.2_declarations/test.ml +++ b/exercises/hferee/1.2_declarations/test.ml @@ -10,8 +10,8 @@ let check_count_fun code_ast name f n = let reports = find_binding code_ast name (ast_check_expr ~on_variable_occurence: (fun v -> if v = f then incr count; [])) in - if !count <> n then [Message ([Text ("Il faut utiliser exactement " ^ string_of_int n ^ " fois '" ^ f ^ "'.")], Failure)] - else [Message ([Text("Bon nombre de '" ^ f ^ "' : " ^ string_of_int n ^ ".")], Success 1)] + if !count <> n then [Message ([Text ("You have to use exactly " ^ string_of_int n ^ " times '" ^ f ^ "'.")], Failure)] + else [Message ([Text("Correct number of '" ^ f ^ "' : " ^ string_of_int n ^ ".")], Success 1)] let testmult name = diff --git a/exercises/hferee/1.3_bool/descr.md b/exercises/hferee/1.3_bool/descr.md index f6afa26..0b509b0 100644 --- a/exercises/hferee/1.3_bool/descr.md +++ b/exercises/hferee/1.3_bool/descr.md @@ -29,33 +29,33 @@ expression that evaluates to `true` if and only if the number `x` is within ```ocaml -let intervalle10 x = false +let interval10 x = false ``` **Question 2.** In the following declarations, replace the boolean expression with its value (`true` or `false`) ```ocaml -let valeur1 = true && false +let value1 = true && false -let valeur2 = true || false +let value2 = true || false -let valeur3 = not (true || true) && true +let value3 = not (true || true) && true ``` **Question 3.** In the following declarations, replace the expression on the right-hand side with a simpler equivalent expression. ```ocaml -let simplifier1 x y = (x || y) || x +let simplify1 x y = (x || y) || x -let simplifier2 x y = (x > 5 && x >= 7) +let simplify2 x y = (x > 5 && x >= 7) -let simplifier3 x y z = x = y && y = z && x = z +let simplify3 x y z = x = y && y = z && x = z -let simplifier4 x y = x > 7 || (x <= 7 && y > 2) +let simplify4 x y = x > 7 || (x <= 7 && y > 2) -let simplifier5 x = (((x = true) = false) = false) = true +let simplify5 x = (((x = true) = false) = false) = true ``` **Question 4.** A leap year is a year whose number is divisible by 4, except if it is @@ -64,7 +64,7 @@ replace `false` with a boolean expression that evaluates to `true` only when `x` integer corresponding to a leap year. ```ocaml -let bissextile x = false +let leap x = false ``` It is worth noting that the `mod` operator calculates the remainder of the division operation. diff --git a/exercises/hferee/1.3_bool/solution.ml b/exercises/hferee/1.3_bool/solution.ml index e239490..b652dbc 100644 --- a/exercises/hferee/1.3_bool/solution.ml +++ b/exercises/hferee/1.3_bool/solution.ml @@ -1,24 +1,24 @@ (* Question 1. *) -let intervalle10 x = x >= 0 && x <= 10 +let interval10 x = x >= 0 && x <= 10 (* Question 2. *) -let valeur1 = false +let value1 = false -let valeur2 = true +let value2 = true -let valeur3 = false +let value3 = false (* Question 3.*) -let simplifier1 x y = x || y +let simplify1 x y = x || y -let simplifier2 x y = x >= 7 +let simplify2 x y = x >= 7 -let simplifier3 x y z = x = y && y = z +let simplify3 x y z = x = y && y = z -let simplifier4 x y = x > 7 || y > 2 +let simplify4 x y = x > 7 || y > 2 -let simplifier5 x = x +let simplify5 x = x (*Question 4. *) -let bissextile x = (x mod 4 = 0) && (x mod 100 <> 0 || x mod 400 = 0) +let leap x = (x mod 4 = 0) && (x mod 100 <> 0 || x mod 400 = 0) diff --git a/exercises/hferee/1.3_bool/template.ml b/exercises/hferee/1.3_bool/template.ml index 56e1ec5..aa7a6ec 100644 --- a/exercises/hferee/1.3_bool/template.ml +++ b/exercises/hferee/1.3_bool/template.ml @@ -1,24 +1,24 @@ (* Question 1. *) -let intervalle10 x = false +let interval10 x = false (* Question 2. *) -let valeur1 = true && false +let value1 = true && false -let valeur2 = true || false +let value2 = true || false -let valeur3 = not (true || true) && true +let value3 = not (true || true) && true (* Question 3.*) -let simplifier1 x y = (x || y) || x +let simplify1 x y = (x || y) || x -let simplifier2 x y = (x > 5 && x >= 7) +let simplify2 x y = (x > 5 && x >= 7) -let simplifier3 x y z = x = y && y = z && x = z +let simplify3 x y z = x = y && y = z && x = z -let simplifier4 x y = x > 7 || (x <= 7 && y > 2) +let simplify4 x y = x > 7 || (x <= 7 && y > 2) -let simplifier5 x = (((x = true) = false) = false) = true +let simplify5 x = (((x = true) = false) = false) = true (*Question 4. *) -let bissextile x = false +let leap x = false diff --git a/exercises/hferee/1.3_bool/test.ml b/exercises/hferee/1.3_bool/test.ml index 43f5e9c..76632e5 100644 --- a/exercises/hferee/1.3_bool/test.ml +++ b/exercises/hferee/1.3_bool/test.ml @@ -22,8 +22,8 @@ let hide l msg = match List.filter (fun m -> match m with | Message (_, Failure let check_count name l test status= Section ([Code name], - hide test "Valeur correcte sur un ensemble de tests" @ - hide (List.concat(List.map (fun (f, n) -> check_one_count name f n "Simplifier davantage." status) l)) "Suffisamment simplifié.") + hide test "Correct value on a set of tests" @ + hide (List.concat(List.map (fun (f, n) -> check_one_count name f n "Simplify more." status) l)) "Simplified enough.") let bools = [true; false] @@ -35,24 +35,24 @@ let bools3 = prod_bools bools2 let exercise = [ - Section ([Code "intervalle10"], hide (test_function_1_against_solution [%ty : int -> bool] "intervalle10" ~gen:0 [-1; 0; 5; 9; 10; 11; 42]) "valeur correcte"); - check_count "valeur1" [("&&", 0); ("||", 0); ("not", 0)] (test_variable_against_solution [%ty : bool] "valeur1") Failure; - check_count "valeur2" [("&&", 0); ("||", 0); ("not", 0)] (test_variable_against_solution [%ty : bool] "valeur2") Failure; - check_count "valeur3" [("&&", 0); ("||", 0); ("not", 0)] (test_variable_against_solution [%ty : bool] "valeur3") Failure; - - check_count "simplifier1" [("&&", 0); ("||", 1); ("not", 0)] - (test_function_2_against_solution [%ty : bool -> bool -> bool] ~gen:0 "simplifier1" bools2) Failure; - check_count "simplifier2" [("&&", 0); ("||", 0); ("not", 0)] - (test_function_2_against_solution [%ty : int -> int -> bool] ~gen:10 "simplifier2" [5, 6]) Failure; - - check_count "simplifier3" [("&&", 1); ("=", 2); ("||", 0)] - (test_function_3_against_solution [%ty : int -> int -> int -> bool] ~gen:20 "simplifier3" []) Failure; - check_count "simplifier4" [("&&", 0); ("||", 1); ("not", 0)] - (test_function_2_against_solution [%ty : int -> int -> bool] ~gen:10 "simplifier4" [(7, 2); (7, 3); (8, 2); (8, 3)]) Failure; - check_count "simplifier5" [("&&", 0); ("||", 0); ("not", 0); ("true", 0); ("false", 0); ("=", 0)] - (test_function_1_against_solution [%ty : bool -> bool] ~gen:0 "simplifier5" bools) Failure; - check_count "bissextile" [("mod", 0); ("||", 1); ("&&", 1)] - (test_function_1_against_solution [%ty : int -> bool] ~gen:100 "bissextile" [0; 4; 100; 200; 400; 700; 800; 804]) Warning + Section ([Code "interval10"], hide (test_function_1_against_solution [%ty : int -> bool] "interval10" ~gen:0 [-1; 0; 5; 9; 10; 11; 42]) "correct value"); + check_count "value1" [("&&", 0); ("||", 0); ("not", 0)] (test_variable_against_solution [%ty : bool] "value1") Failure; + check_count "value2" [("&&", 0); ("||", 0); ("not", 0)] (test_variable_against_solution [%ty : bool] "value2") Failure; + check_count "value3" [("&&", 0); ("||", 0); ("not", 0)] (test_variable_against_solution [%ty : bool] "value3") Failure; + + check_count "simplify1" [("&&", 0); ("||", 1); ("not", 0)] + (test_function_2_against_solution [%ty : bool -> bool -> bool] ~gen:0 "simplify1" bools2) Failure; + check_count "simplify2" [("&&", 0); ("||", 0); ("not", 0)] + (test_function_2_against_solution [%ty : int -> int -> bool] ~gen:10 "simplify2" [5, 6]) Failure; + + check_count "simplify3" [("&&", 1); ("=", 2); ("||", 0)] + (test_function_3_against_solution [%ty : int -> int -> int -> bool] ~gen:20 "simplify3" []) Failure; + check_count "simplify4" [("&&", 0); ("||", 1); ("not", 0)] + (test_function_2_against_solution [%ty : int -> int -> bool] ~gen:10 "simplify4" [(7, 2); (7, 3); (8, 2); (8, 3)]) Failure; + check_count "simplify5" [("&&", 0); ("||", 0); ("not", 0); ("true", 0); ("false", 0); ("=", 0)] + (test_function_1_against_solution [%ty : bool -> bool] ~gen:0 "simplify5" bools) Failure; + check_count "leap" [("mod", 0); ("||", 1); ("&&", 1)] + (test_function_1_against_solution [%ty : int -> bool] ~gen:100 "leap" [0; 4; 100; 200; 400; 700; 800; 804]) Warning ] let () = diff --git a/exercises/hferee/1.4_conditionals/descr.md b/exercises/hferee/1.4_conditionals/descr.md index 82a08a2..099f7bd 100644 --- a/exercises/hferee/1.4_conditionals/descr.md +++ b/exercises/hferee/1.4_conditionals/descr.md @@ -6,7 +6,7 @@ and `e2`. **Question 1.** -For each of the following expressions, give its value, or `Erreur` if it doesn't +For each of the following expressions, give its value, or `Error` if it doesn't make sense. ```ocaml @@ -27,9 +27,9 @@ Replace the following conditional expressions with equivalent expressions contai strictly fewer `if` statements. ```ocaml -let simplifie1 x = if x > 3 then false else true +let simplify1 x = if x > 3 then false else true -let simplifie2 x y= +let simplify2 x y= if x then if y then false else true diff --git a/exercises/hferee/1.4_conditionals/prepare.ml b/exercises/hferee/1.4_conditionals/prepare.ml index f718b2a..543389d 100644 --- a/exercises/hferee/1.4_conditionals/prepare.ml +++ b/exercises/hferee/1.4_conditionals/prepare.ml @@ -1 +1 @@ -exception Erreur +exception Error diff --git a/exercises/hferee/1.4_conditionals/solution.ml b/exercises/hferee/1.4_conditionals/solution.ml index 202bd02..93b9916 100644 --- a/exercises/hferee/1.4_conditionals/solution.ml +++ b/exercises/hferee/1.4_conditionals/solution.ml @@ -1,13 +1,13 @@ let phrase1 = 4 -let phrase2 = Erreur +let phrase2 = Error -let phrase3 = Erreur +let phrase3 = Error -let simplifie1 x = x <= 3 +let simplify1 x = x <= 3 -let simplifie2 x y = if x then not y else y +let simplify2 x y = if x then not y else y let edt day time = if day = "monday" && 13 * 60 + 30 <= time && time < 15 * 60 + 30 then "practical" diff --git a/exercises/hferee/1.4_conditionals/template.ml b/exercises/hferee/1.4_conditionals/template.ml index 020b66c..02f9e47 100644 --- a/exercises/hferee/1.4_conditionals/template.ml +++ b/exercises/hferee/1.4_conditionals/template.ml @@ -1,17 +1,17 @@ (* Question 1 *) -let phrase1 = "Remplacez-moi" +let phrase1 = "Replace me" -let phrase2 = "Remplacez-moi" +let phrase2 = "Replace me" -let phrase3 = "Remplacez-moi" +let phrase3 = "Replace me" (* Question 2 *) -let simplifie1 x = "Remplacez-moi" +let simplify1 x = "Replace me" -let simplifie2 x y = "Remplacez-moi" +let simplify2 x y = "Replace me" -let edt day time = "Remplacez-moi" +let edt day time = "Replace me" (* Question 3 *) diff --git a/exercises/hferee/1.4_conditionals/test.ml b/exercises/hferee/1.4_conditionals/test.ml index 1a907a7..87b5179 100644 --- a/exercises/hferee/1.4_conditionals/test.ml +++ b/exercises/hferee/1.4_conditionals/test.ml @@ -36,7 +36,7 @@ let rec hide l msg = match l with let check_count name l test status= Section ([Code name], test @ if l = [] then [] - else hide (List.concat(List.map (fun (f, n) -> check_one_count name f n "Simplifier davantage." status) l)) "Suffisamment simplifié.") + else hide (List.concat(List.map (fun (f, n) -> check_one_count name f n "Simplify more." status) l)) "Simplified enough.") let bools = [true; false] @@ -53,10 +53,10 @@ let exercise = testexn "phrase2"; testexn "phrase3"; - check_count "simplifie1" [(test_construct "true", 0); (test_construct "false", 0)] - (test_function_1_against_solution [%ty : int -> bool] ~gen:0 "simplifie1" [-1; 0; 1; 2; 100]) Failure; - check_count "simplifie2" [(test_construct "true", 0); (test_construct "false", 0)] - (test_function_2_against_solution [%ty : bool -> bool -> bool] ~gen:0 "simplifie2" bools2) Failure; + check_count "simplify1" [(test_construct "true", 0); (test_construct "false", 0)] + (test_function_1_against_solution [%ty : int -> bool] ~gen:0 "simplify1" [-1; 0; 1; 2; 100]) Failure; + check_count "simplify2" [(test_construct "true", 0); (test_construct "false", 0)] + (test_function_2_against_solution [%ty : bool -> bool -> bool] ~gen:0 "simplify2" bools2) Failure; check_count "edt" [(test_string "Nothing interesting" , 1)] (test_function_2_against_solution [%ty : string -> int -> string] ~gen:5 "edt" [("monday", 13*60+29); ("monday", 13*60+30); ("monday", 15*60+30); ("monday", 14); diff --git a/exercises/hferee/11_printable/test.ml b/exercises/hferee/11_printable/test.ml index c986b3c..0ed7497 100644 --- a/exercises/hferee/11_printable/test.ml +++ b/exercises/hferee/11_printable/test.ml @@ -19,7 +19,7 @@ let exercise = [test_show, (4, 2); test_show (-4, 2)] ); *) Section( - [Text "Pas de correction automatique pour cet exercice"], []) + [Text "No automatic correction for this exercise"], []) ] let () = diff --git a/exercises/hferee/3.0_sudoku/descr.md b/exercises/hferee/3.0_sudoku/descr.md index e42edf0..35fd142 100644 --- a/exercises/hferee/3.0_sudoku/descr.md +++ b/exercises/hferee/3.0_sudoku/descr.md @@ -8,16 +8,16 @@ For clarity, we can declare _type aliases_ to describe all the elements of a Sud ```ocaml (* small line of three numbers *) -type petite_ligne = int * int * int +type small_line = int * int * int -(*small size block 3 x 3*) -type bloc = petite_ligne * petite_ligne * petite_ligne +(* small size block 3 x 3 *) +type block = small_line * small_line * small_line -(*large 3 x 9 line*) -type grande_ligne = bloc * bloc * bloc +(* large 3 x 9 line *) +type big_line = block * block * block (* full grid *) -type grille = grande_ligne * grande_ligne * grande_ligne +type grid = big_line * big_line * big_line ``` In the following sections, unless otherwise stated, **annotate each function using the types mentioned above**. @@ -36,11 +36,11 @@ Since our grid consists of triplets of triplets of triplets of triplets, we will 1. Write the function `app3` that takes a function as its first argument, a triplet as its second argument, and applies the function to each element of the triplet, returning the corresponding triplet. Avoid _annotating the type_ of this function and observe the type inferred by OCaml. -2. Define a function `tous` that takes a function of type `bool` and a triplet, and checks if the function returns `true` for all elements of the triplet. +2. Define a function `all` that takes a function of type `bool` and a triplet, and checks if the function returns `true` for all elements of the triplet. -3. Given the same inputs, the function `existe` should return `true` if the function returns `true` for at least one element. +3. Given the same inputs, the function `exist` should return `true` if the function returns `true` for at least one element. -4. Finally, define a function `existe_paire` such that if `test` is a boolean function with two arguments and `t` is a triplet, then `existe_paire test t` returns `true` when there exists a pair `(a, b)` such that `test a b` is `true` for two distinct elements `a` and `b` from the triplet `t`. We can assume that the function `test` is symmetric: `test a b = test b a`. +4. Finally, define a function `exist_pair` such that if `test` is a boolean function with two arguments and `t` is a triplet, then `exist_pair test t` returns `true` when there exists a pair `(a, b)` such that `test a b` is `true` for two distinct elements `a` and `b` from the triplet `t`. We can assume that the function `test` is symmetric: `test a b = test b a`. The above functions are called high-order functions as they take a function as an argument. They will allow us to define functions with similar structures more easily. @@ -50,21 +50,21 @@ The above functions are called high-order functions as they take a function as a Let's first verify that all elements in the grid are integers between 1 and 9. -1. Write a function `dans_bornes` that checks if a number is between 1 and 9. +1. Write a function `within_bounds` that checks if a number is between 1 and 9. -2. Using `tous` and `dans_bornes`, define functions `dans_bornes_petite_ligne` and `dans_bornes_bloc` that return a boolean indicating whether all elements in their argument are between 1 and 9. There is no need to explicitly specify the type argument for `bloc` to define this function! +2. Using `all` and `within_bounds`, define functions `within_bounds_small_line` and `within_bounds_block` that return a boolean indicating whether all elements in their argument are between 1 and 9. There is no need to explicitly specify the type argument for `block` to define this function! -3. Similarly, using `tous` and `dans_bornes_bloc`, define a function `dans_bornes_grille: grille -> bool` that checks if all elements in a grid are between 1 and 9. +3. Similarly, using `all` and `within_bounds_block`, define a function `within_bounds_grid: grid -> bool` that checks if all elements in a grid are between 1 and 9. --- **Question 3: duplication verification** We now want to check that there are no duplicates in a block. -1. Using the functions from question 1, define a function `differents_petite_ligne: petite_ligne -> bool` that checks if the elements of a small line are all pairwise different. In other words, there are no two equal elements. -2. Define a function `dans` such that `dans pl x` tests if `x` is in the small line `pl`. -3. Deduce a function `intersecte` that takes two small lines and checks if they intersect. That is, if there is an element from the first line that is in the second line. -4. Deduce a function `differents_bloc: bloc -> bool` that checks if all the elements of a block are all different. This means verifying that in each small line, all the elements are different, and that there are no two intersecting small lines. +1. Using the functions from question 1, define a function `differents_small_line: small_line -> bool` that checks if the elements of a small line are all pairwise different. In other words, there are no two equal elements. +2. Define a function `within` such that `within pl x` tests if `x` is in the small line `pl`. +3. Deduce a function `intersects` that takes two small lines and checks if they intersect. That is, if there is an element from the first line that is in the second line. +4. Deduce a function `differents_block: block -> bool` that checks if all the elements of a block are all different. This means verifying that in each small line, all the elements are different, and that there are no two intersecting small lines. --- @@ -72,8 +72,8 @@ We now want to check that there are no duplicates in a block. Note that a block (3 x 3) contains exactly the numbers from 1 to 9 if all its elements are between 1 and 9 and if they are all different. -1. Deduce a function `bloc_correct: bloc -> bool` that checks if a block is well-formed. -2. Use it to define `blocs_corrects: grille -> bool` that checks this for all the blocks in a grid. +1. Deduce a function `block_correct: block -> bool` that checks if a block is well-formed. +2. Use it to define `blocks_correct: grid -> bool` that checks this for all the blocks in a grid. --- @@ -94,21 +94,21 @@ For example, Check (using the "Compile" button) that it can be annotated with the following types: ```ocaml -: bloc -> blocf -: grande_ligne -> grande_ligne -: grille -> grille +: block -> blocf +: big_line -> big_line +: grid -> grid ``` Then remove its type annotations. --- -**Note:** To test your transformations on grids (like `transpose9`), feel free to use the `grille_aleatoire` and `print_grille` functions provided in the preamble. +**Note:** To test your transformations on grids (like `transpose9`), feel free to use the `random_grid` and `print_grid` functions provided in the preamble. --- **Question 6. (Row Verification)** -Use `transpose9` to define `transpose_lignes_blocs: grille -> grille` that creates a grid in which each block is a row of the input (and vice versa). +Use `transpose9` to define `transpose_lines_blocks: grid -> grid` that creates a grid in which each block is a row of the input (and vice versa). For example, ``` @@ -125,15 +125,15 @@ For example, 9 3 9 | 3 1 3 | 5 8 1 9 3 3 | 6 7 3 | 5 8 1 ``` -Use this function to define `lignes_correctes: grille -> bool` that checks if all the rows of a grid are correct. +Use this function to define `lines_correct: grid -> bool` that checks if all the rows of a grid are correct. --- **Question 7. (Column Verification)** -Using `transpose9`, define a function `transpose_blocs: grille -> grille` that transposes each block of a grid (each block remains in place). +Using `transpose9`, define a function `transpose_blocks: grid -> grid` that transposes each block of a grid (each block remains in place). -Deduce a function `transpose_grille: grille -> grille` that, similar to `transpose9`, creates a grid in which the rows are the columns of the argument. +Deduce a function `transpose_grid: grid -> grid` that, similar to `transpose9`, creates a grid in which the rows are the columns of the argument. For example, ``` @@ -152,10 +152,10 @@ For example, Note that we only need to transpose the entire blocks and then within each block. -Finally, define `colonnes_correctes: grille -> bool` following the pattern of `lignes_correctes`, and then `correcte: grille -> bool` that checks all the +Finally, define `columns_correct: grid -> bool` following the pattern of `lines_correct`, and then `correct: grid -> bool` that checks all the expected properties of a Sudoku grid. **Bonus Questions** - - Based on the previous functions, redefine the function `print_grille` (the version in the preamble is intentionally obscured). + - Based on the previous functions, redefine the function `print_grid` (the version in the preamble is intentionally obscured). - (difficult) An incomplete grid can contain empty cells denoted by `0`. Define a function that produces a complete and correct grid from a grid with empty cells, if possible. diff --git a/exercises/hferee/3.0_sudoku/prelude.ml b/exercises/hferee/3.0_sudoku/prelude.ml index 3b7835d..46b81db 100644 --- a/exercises/hferee/3.0_sudoku/prelude.ml +++ b/exercises/hferee/3.0_sudoku/prelude.ml @@ -1,32 +1,32 @@ -(* petite ligne de trois nombres *) -type petite_ligne = int * int * int +(* small line of three numbers *) +type small_line = int * int * int -(* petite bloc de taille 3 x 3 *) -type bloc = petite_ligne * petite_ligne * petite_ligne +(* small size block 3 x 3 *) +type block = small_line * small_line * small_line -(* grande ligne de 3 x 9 *) -type grande_ligne = bloc * bloc * bloc +(* large 3 x 9 line *) +type big_line = block * block * block -(* grille complète *) -type grille = grande_ligne * grande_ligne * grande_ligne +(* full grid *) +type grid = big_line * big_line * big_line let make3 f = fun () -> (f(), f(), f()) -let grille_aleatoire : unit -> grille = (fun () -> Random.int 9 + 1) |> make3 |> make3 |> make3 |> make3 +let random_grid : unit -> grid = (fun () -> Random.int 9 + 1) |> make3 |> make3 |> make3 |> make3 -let print_grille f (g : grille) : unit = +let print_grid f (g : grid) : unit = let _ = Format.pp_print_string f "\n" in let print_string = Format.pp_print_string f in let print_int n = print_string (string_of_int n) in let print3 f sep (a, b, c) = f a; print_string sep; f b; print_string sep; f c in - let print_grille_aux (g : grille ) : unit = + let print_grid_aux (g : grid ) : unit = print3 (print3 (print3 (print3 print_int " ") " | ") "\n") "\n------+-------+------\n" g ; print_string "\n" in let a t (x, y, z) = t x, t y, t z in - print_grille_aux (a (fun ll -> a (fun (x, _, _) -> x) ll, a (fun (_, x, _) -> x) ll, a (fun (_, _, x) -> x) ll) g);; + print_grid_aux (a (fun ll -> a (fun (x, _, _) -> x) ll, a (fun (_, x, _) -> x) ll, a (fun (_, _, x) -> x) ll) g);; -#install_printer print_grille;; +#install_printer print_grid;; diff --git a/exercises/hferee/3.0_sudoku/solution.ml b/exercises/hferee/3.0_sudoku/solution.ml index e482018..9014867 100644 --- a/exercises/hferee/3.0_sudoku/solution.ml +++ b/exercises/hferee/3.0_sudoku/solution.ml @@ -4,71 +4,71 @@ let p2 (_, y, _) = y let p3 (_, _, z) = z let app3 f (x, y, z) = (f x, f y, f z) -let tous check (a, b, c) = check a && check b && check c -let existe test (x, y, z) = test x || test y || test z -let existe_paire test (x, y, z) = test x y || test x z || test y z +let all check (a, b, c) = check a && check b && check c +let exist test (x, y, z) = test x || test y || test z +let exist_pair test (x, y, z) = test x y || test x z || test y z (* Question 2 *) -let dans_bornes x = 1 <= x && x <= 9 -let dans_bornes_petite_ligne = tous dans_bornes -let dans_bornes_bloc : bloc -> bool = tous dans_bornes_petite_ligne -let dans_bornes_grille = tous (tous dans_bornes_bloc) +let within_bounds x = 1 <= x && x <= 9 +let within_bounds_small_line = all within_bounds +let within_bounds_block : block -> bool = all within_bounds_small_line +let within_bounds_grid = all (all within_bounds_block) (* Question 3 *) -let differents_petite_ligne l = not (existe_paire (=) l) -let dans t x = existe ((=) x) t -let intersecte t1 = existe (dans t1) -let differents_bloc b = tous differents_petite_ligne b && - not (existe_paire intersecte b) +let differents_small_line l = not (exist_pair (=) l) +let within t x = exist ((=) x) t +let intersects t1 = exist (within t1) +let differents_block b = all differents_small_line b && + not (exist_pair intersects b) (* Question 4 *) -let bloc_correct c = dans_bornes_bloc c && differents_bloc c +let block_correct c = within_bounds_block c && differents_block c -let blocs_corrects : grille -> bool = tous (tous bloc_correct) +let blocks_correct : grid -> bool = all (all block_correct) (* Question 6 *) let transpose9 ll = app3 p1 ll, app3 p2 ll, app3 p3 ll (* Question 7 *) -let transpose_lignes_blocs : grille -> grille = app3 transpose9 +let transpose_lines_blocks : grid -> grid = app3 transpose9 -let lignes_correctes (g : grille) : bool = - blocs_corrects (transpose_lignes_blocs g) +let lines_correct (g : grid) : bool = + blocks_correct (transpose_lines_blocks g) (* Question 8 *) -let transpose_blocs : grille -> grille = app3 (app3 transpose9) +let transpose_blocks : grid -> grid = app3 (app3 transpose9) -let transpose_grille (g : grille) : grille = - transpose9 (transpose_blocs g) -let colonnes_correctes (g : grille) = - lignes_correctes (transpose_grille g) +let transpose_grid (g : grid) : grid = + transpose9 (transpose_blocks g) +let columns_correct (g : grid) = + lines_correct (transpose_grid g) -let correcte (g : grille) : bool = - lignes_correctes g && colonnes_correctes g && blocs_corrects g +let correct (g : grid) : bool = + lines_correct g && columns_correct g && blocks_correct g (* support des grilles partielles (avec 0) et solveur *) (* module Partiel = struct let bornes x = 0 <= x && x <= 9 - let bornes_petite_ligne = tous bornes - let bornes_bloc : bloc -> bool = tous bornes_petite_ligne + let bornes_small_line = all bornes + let bornes_bloc : block -> bool = all bornes_small_line let check_dup1 a (mem : int -> bool) : (int -> bool) option = if mem a && a <> 0 then None else Some (fun x -> x = a || mem x) - let check_dup_pl ((a, b, c) : petite_ligne) mem : (int -> bool) option = + let check_dup_pl ((a, b, c) : small_line) mem : (int -> bool) option = Option.bind (Option.bind (Option.bind (Some mem) (check_dup1 a)) (check_dup1 b)) (check_dup1 c) - (* check if there is a non-zero duplicate in a bloc*) - let check_dup_bloc ((a, b, c) : bloc) : (int -> bool) option = + (* check if there is a non-zero duplicate in a block*) + let check_dup_bloc ((a, b, c) : block) : (int -> bool) option = Option.bind (Option.bind (Option.bind (Some (fun _ -> false)) (check_dup_pl a)) (check_dup_pl b)) (check_dup_pl c) (* Question 5 *) - let bloc_correct c = bornes_bloc c && check_dup_bloc c <> None + let block_correct c = bornes_bloc c && check_dup_bloc c <> None - let trois_blocs_corrects = tous bloc_correct + let trois_blocs_corrects = all block_correct - let blocs_corrects : grille -> bool = tous trois_blocs_corrects + let blocks_correct : grid -> bool = all trois_blocs_corrects (* Question 6 *) let p1 (x, _, _) = x @@ -78,21 +78,21 @@ let correcte (g : grille) : bool = let transpose9 ll = app3 p1 ll, app3 p2 ll, app3 p3 ll - let transpose_lignes_blocs : grille -> grille = app3 transpose9 + let transpose_lines_blocks : grid -> grid = app3 transpose9 - let lignes_correctes (g : grille) : bool = - blocs_corrects (transpose_lignes_blocs g) + let lines_correct (g : grid) : bool = + blocks_correct (transpose_lines_blocks g) (* Question 7 *) - let transpose_blocs : grille -> grille = app3 (app3 transpose9) + let transpose_blocks : grid -> grid = app3 (app3 transpose9) - let transpose_grille (g : grille) : grille = - transpose9 (transpose_blocs g) - let colonnes_correctes (g : grille) = - lignes_correctes (transpose_grille g) + let transpose_grid (g : grid) : grid = + transpose9 (transpose_blocks g) + let columns_correct (g : grid) = + lines_correct (transpose_grid g) - let correcte (g : grille) : bool = - lignes_correctes g && colonnes_correctes g && blocs_corrects g + let correct (g : grid) : bool = + lines_correct g && columns_correct g && blocks_correct g let fill_case (v : int) (a : int) : int option = if a = 0 then Some v else None @@ -106,18 +106,18 @@ let correcte (g : grille) : bool = | None -> None - let fill_petite_ligne = fill_thing fill_case - let fill_bloc : int -> bloc -> bloc option = fill_thing fill_petite_ligne + let fill_small_line = fill_thing fill_case + let fill_bloc : int -> block -> block option = fill_thing fill_small_line let fill_gl = fill_thing fill_bloc - let fill_grille : int -> grille -> grille option = fill_thing fill_gl + let fill_grille : int -> grid -> grid option = fill_thing fill_gl let rec next_grille v g = if v > 9 then None else match fill_grille v g with - | Some g' when correcte g' -> Some (g', v) + | Some g' when correct g' -> Some (g', v) | _ -> next_grille (v + 1) g - let complete : grille -> bool = tous (tous (tous (tous (( <> ) 0) ))) + let complete : grid -> bool = all (all (all (all (( <> ) 0) ))) let build_grille = let rec build_grille_aux min g = if min > 9 then None else diff --git a/exercises/hferee/3.0_sudoku/template.ml b/exercises/hferee/3.0_sudoku/template.ml index 6ad25e5..68dcc7f 100644 --- a/exercises/hferee/3.0_sudoku/template.ml +++ b/exercises/hferee/3.0_sudoku/template.ml @@ -1,2 +1,2 @@ -(* Pas de modèle cette fois-ci. - Déclarez vous-même les fonctions, annotées avec le bon type *) +(* No model this time. + Declare the functions yourself, annotated with the correct type *) diff --git a/exercises/hferee/3.0_sudoku/test.ml b/exercises/hferee/3.0_sudoku/test.ml index f3c1f76..920abd0 100644 --- a/exercises/hferee/3.0_sudoku/test.ml +++ b/exercises/hferee/3.0_sudoku/test.ml @@ -35,25 +35,25 @@ let nopat fname = let rec forbid_pat (p : Parsetree.pattern) = match p.ppat_desc with | Ppat_var v -> [] (* nommer des variables c'est ok *) | Ppat_constraint (p, _) -> forbid_pat p (* annoter des types, on verra *) - | _ -> [Message([Text ("Interdiction d'utiliser du filtrage de motifs dans " ^ fname) ], Failure)] in + | _ -> [Message([Text ("Do not use pattern matching inside " ^ fname) ], Failure)] in find_binding code_ast fname (ast_check_expr ~on_pattern:forbid_pat) let nopatatall fname = - let msg = [Message([Text ("Ne pas utiliser d'arguments pour définir " ^ fname) ], Failure)] in + let msg = [Message([Text ("Don't use arguments to define " ^ fname) ], Failure)] in find_binding code_ast fname (ast_check_expr ~on_pattern: (fun _ -> msg)) (* 0 is left on purpose to make bad grilles*) let sc () = (abs(sample_int()) mod 10) let triple f () = f(), f(), f() -let sample_petite_ligne = triple sc -let sample_bloc = triple sample_petite_ligne -let sample_gl = triple sample_bloc -let sample_grille = triple sample_gl +let sample_small_line = triple sc +let sample_block = triple sample_small_line +let sample_gl = triple sample_block +let sample_grid = triple sample_gl let sample_s3 () = sample_string(), sample_string(), sample_string() -let good_bloc = ((1, 2, 3), (4, 5, 6), (7, 8, 9)) +let good_block = ((1, 2, 3), (4, 5, 6), (7, 8, 9)) let good_gl = ((1, 2, 3), (4, 5, 6), (7, 8, 9)), ((7, 8, 9), (1, 2, 3), (4, 5, 6)), ((4, 5, 6), (7, 8, 9), (1, 2, 3)) -let good_grille = (((4, 1, 5), (3, 6, 2), (7, 8, 9)), +let good_grid = (((4, 1, 5), (3, 6, 2), (7, 8, 9)), ((6, 3, 8), (4, 7, 9), (2, 1, 5)), ((9, 7, 2), (1, 6, 5), (3, 6, 4))), (((9, 2, 6), (1, 3, 8), (5, 7, 4)), @@ -77,38 +77,38 @@ let exercise = test_function_2_against_solution [%ty : (int -> string) -> (int * int * int) -> (string * string * string) ] ~gen:0 "app3" [(string_of_int, (1, 2, 3))] @ test_function_2_against_solution [%ty : (string -> bool) -> (string * string * string) -> bool] - ~gen:0 "tous" [(=) "ok", ("", "", ""); (=) "ok", ("ok", "ok", "ok"); (=) "ok", ("", "", "ok")] @ + ~gen:0 "all" [(=) "ok", ("", "", ""); (=) "ok", ("ok", "ok", "ok"); (=) "ok", ("", "", "ok")] @ test_function_2_against_solution [%ty : (string -> bool) -> (string * string * string) -> bool] - ~gen:5 "existe" ~sampler: (fun () -> (fun s -> s < "m"), sample_s3()) [] @ + ~gen:5 "exist" ~sampler: (fun () -> (fun s -> s < "m"), sample_s3()) [] @ test_function_2_against_solution [%ty : (int -> int -> bool) -> (int * int * int) -> bool] - ~gen:0 "existe_paire" [(=), (1, 2, 3); (=), (1, 2, 1); (=), (1, 1, 2); (=), (1, 2, 2)] + ~gen:0 "exist_pair" [(=), (1, 2, 3); (=), (1, 2, 1); (=), (1, 1, 2); (=), (1, 2, 2)] ) ; Section([Code "Question 2"], - test_function_1_against_solution [%ty : int -> bool] ~gen:0 "dans_bornes" [0; 1; 9; 10; 11] @ - test_function_1_against_solution [%ty : petite_ligne -> bool] ~gen:3 - "dans_bornes_petite_ligne" [(1, 0, 1); (0, 1, 1); (1, 1, 0); (1, 1, 1)] @ - nopatatall "dans_bornes_petite_ligne" @ - test_function_1_against_solution [%ty : bloc -> bool] ~gen:5 ~sampler: - sample_bloc "dans_bornes_bloc" [good_bloc] @ - nopatatall "dans_bornes_bloc" @ - test_function_1_against_solution [%ty : grille -> bool] ~gen:5 ~sampler: - sample_grille "dans_bornes_grille" [good_grille] + test_function_1_against_solution [%ty : int -> bool] ~gen:0 "within_bounds" [0; 1; 9; 10; 11] @ + test_function_1_against_solution [%ty : small_line -> bool] ~gen:3 + "within_bounds_small_line" [(1, 0, 1); (0, 1, 1); (1, 1, 0); (1, 1, 1)] @ + nopatatall "within_bounds_small_line" @ + test_function_1_against_solution [%ty : block -> bool] ~gen:5 ~sampler: + sample_block "within_bounds_block" [good_block] @ + nopatatall "within_bounds_block" @ + test_function_1_against_solution [%ty : grid -> bool] ~gen:5 ~sampler: + sample_grid "within_bounds_grid" [good_grid] ); Section([Code "Question 3"], - test_function_1_against_solution [%ty : petite_ligne -> bool] ~gen:3 - "differents_petite_ligne" [(1, 0, 2); (0, 1, 1); (1, 1, 1)] @ - test_function_2_against_solution [%ty : petite_ligne -> int -> bool] ~gen:5 - "dans" [((0, 1, 2), 1); ((0, 1, 2), 3)] @ - test_function_2_against_solution [%ty : petite_ligne -> petite_ligne -> bool] ~gen:5 - "intersecte" [((0, 1, 2), (1, 3, 4)); ((0, 1, 2), (3, 4, 5))] @ - test_function_1_against_solution [%ty : bloc -> bool] ~gen:5 - "differents_bloc" [good_bloc; ((1, 2, 3), (4, 5, 6), (7, 8, 2)); (1, 2, 3), (4, 5, 4), (7, 8, 9)] + test_function_1_against_solution [%ty : small_line -> bool] ~gen:3 + "differents_small_line" [(1, 0, 2); (0, 1, 1); (1, 1, 1)] @ + test_function_2_against_solution [%ty : small_line -> int -> bool] ~gen:5 + "within" [((0, 1, 2), 1); ((0, 1, 2), 3)] @ + test_function_2_against_solution [%ty : small_line -> small_line -> bool] ~gen:5 + "intersects" [((0, 1, 2), (1, 3, 4)); ((0, 1, 2), (3, 4, 5))] @ + test_function_1_against_solution [%ty : block -> bool] ~gen:5 + "differents_block" [good_block; ((1, 2, 3), (4, 5, 6), (7, 8, 2)); (1, 2, 3), (4, 5, 4), (7, 8, 9)] ); Section([Code "Question 4"], - test_function_1_against_solution [%ty : bloc -> bool] ~gen:5 ~sampler: sample_bloc "bloc_correct" [good_bloc] @ - nopat "bloc_correct" @ - test_function_1_against_solution [%ty : grille -> bool] ~gen:5 ~sampler: sample_grille "blocs_corrects" [good_grille] @ - nopatatall "blocs_corrects" + test_function_1_against_solution [%ty : block -> bool] ~gen:5 ~sampler: sample_block "block_correct" [good_block] @ + nopat "block_correct" @ + test_function_1_against_solution [%ty : grid -> bool] ~gen:5 ~sampler: sample_grid "blocks_correct" [good_grid] @ + nopatatall "blocks_correct" ) ; @@ -117,26 +117,26 @@ let exercise = (string * string * string) * (int * int * int) * (float * float * float)] ~gen:3 ~sampler:(fun () -> sample3(), sample3(), sample3()) "transpose9" []); Section([Code "Question 6"], - test_function_1_against_solution [%ty : grille -> grille] - ~gen:0 "transpose_lignes_blocs" [good_grille] @ - nopatatall "transpose_lignes_blocs" @ - test_function_1_against_solution [%ty : grille -> bool] - ~gen:5 ~sampler: sample_grille "lignes_correctes" [good_grille] @ - nopat "lignes_correctes" + test_function_1_against_solution [%ty : grid -> grid] + ~gen:0 "transpose_lines_blocks" [good_grid] @ + nopatatall "transpose_lines_blocks" @ + test_function_1_against_solution [%ty : grid -> bool] + ~gen:5 ~sampler: sample_grid "lines_correct" [good_grid] @ + nopat "lines_correct" ); Section([Code "Question 7"], - test_function_1_against_solution [%ty : grille -> grille] - ~gen:0 "transpose_blocs" [good_grille] @ - nopatatall "transpose_blocs" @ - test_function_1_against_solution [%ty : grille -> grille] - ~gen:0 "transpose_grille" [good_grille] @ - nopat "transpose_grille" @ - test_function_1_against_solution [%ty : grille -> bool] - ~gen:5 ~sampler: sample_grille "colonnes_correctes" [good_grille] @ - nopat "colonnes_correctes" @ - test_function_1_against_solution [%ty : grille -> bool] - ~gen:5 ~sampler: sample_grille "correcte" [good_grille] @ - nopat "correcte" + test_function_1_against_solution [%ty : grid -> grid] + ~gen:0 "transpose_blocks" [good_grid] @ + nopatatall "transpose_blocks" @ + test_function_1_against_solution [%ty : grid -> grid] + ~gen:0 "transpose_grid" [good_grid] @ + nopat "transpose_grid" @ + test_function_1_against_solution [%ty : grid -> bool] + ~gen:5 ~sampler: sample_grid "columns_correct" [good_grid] @ + nopat "columns_correct" @ + test_function_1_against_solution [%ty : grid -> bool] + ~gen:5 ~sampler: sample_grid "correct" [good_grid] @ + nopat "correct" ) ] diff --git a/exercises/hferee/4.2_coordinates/descr.md b/exercises/hferee/4.2_coordinates/descr.md index 6880818..d6a73af 100644 --- a/exercises/hferee/4.2_coordinates/descr.md +++ b/exercises/hferee/4.2_coordinates/descr.md @@ -13,7 +13,7 @@ Write a function `cartesian_of_polar` that converts a point in polar coordinates **Question 2:** -Define the function `milieu_cart: cartesian -> cartesian -> cartesian` that calculates the midpoint of two points. +Define the function `middle_cart: cartesian -> cartesian -> cartesian` that calculates the midpoint of two points. We now define the type `point` that can represent points in the plane using either of the representations. @@ -23,5 +23,5 @@ type point = Cartesian of cartesian | Polar of polar **Question 3:** -Define a function `milieu: point -> point -> point` that calculates the midpoint of two points, regardless of their representation. +Define a function `middle: point -> point -> point` that calculates the midpoint of two points, regardless of their representation. diff --git a/exercises/hferee/4.2_coordinates/solution.ml b/exercises/hferee/4.2_coordinates/solution.ml index 8f8d53e..02e24bd 100644 --- a/exercises/hferee/4.2_coordinates/solution.ml +++ b/exercises/hferee/4.2_coordinates/solution.ml @@ -2,10 +2,10 @@ let cartesian_of_polar {r; angle} = { x = r *. cos(angle); y = r *. sin(angle); } -let milieu_cart a b = { x = (a .x +. b.x) /. 2.; y = (a.y +. b.y) /. 2.; } +let middle_cart a b = { x = (a .x +. b.x) /. 2.; y = (a.y +. b.y) /. 2.; } let ensure_cart = function | Polar p -> cartesian_of_polar p | Cartesian foo -> foo -let milieu a b = Cartesian (milieu_cart (ensure_cart a) (ensure_cart b)) +let middle a b = Cartesian (middle_cart (ensure_cart a) (ensure_cart b)) diff --git a/exercises/hferee/6_hanoi/solution.ml b/exercises/hferee/6_hanoi/solution.ml index bb3c424..0fa2cee 100644 --- a/exercises/hferee/6_hanoi/solution.ml +++ b/exercises/hferee/6_hanoi/solution.ml @@ -1,7 +1,7 @@ let string_of_tower = function - | L -> "gauche" - | R -> "droite" - | M -> "milieu" + | L -> "left" + | R -> "right" + | M -> "middle" let move x y = print_string (string_of_tower x ^ " -> " ^ string_of_tower y); diff --git a/exercises/hferee/8_sort/test.ml b/exercises/hferee/8_sort/test.ml index f2d4268..d584eed 100644 --- a/exercises/hferee/8_sort/test.ml +++ b/exercises/hferee/8_sort/test.ml @@ -14,7 +14,7 @@ let check name f n = (fun v -> if test_construct f v then incr count; [])) in if !count <> n then - let msg = if n = 0 then "Ne pas utiliser " else "Utiliser " in + let msg = if n = 0 then "Do not use " else "Use " in [Message ([Text (msg ^ f)], Failure)] else [] From 22a30c15d33e8029b202ea4cef995b2d1504d2f2 Mon Sep 17 00:00:00 2001 From: Louis Gesbert Date: Thu, 26 Oct 2023 20:35:58 +0200 Subject: [PATCH 7/8] Fixes for lear-ocaml precompilation - stuff not allowed in modules but allowed in toplevel: * function with weak types * type redefinition (this was an error!) - functions that were called `sample_xxx` while it wasn't a definition of a sampler that should be registered, and there was no `xxx` type. --- .gitignore | 5 ++++- exercises/hferee/11_lists_lists/solution.ml | 2 +- exercises/hferee/3.0_sudoku/prelude.ml | 3 --- exercises/smelodesousa/F4/4-fun-1/test.ml | 2 +- exercises/smelodesousa/F5/5-lists/test.ml | 20 ++++++++++---------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 5cf0190..7043b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ +_opam www sync *.report.html -*~ \ No newline at end of file +*~ +*.cm* +exercises/**/*.js diff --git a/exercises/hferee/11_lists_lists/solution.ml b/exercises/hferee/11_lists_lists/solution.ml index b886adc..4e2c878 100644 --- a/exercises/hferee/11_lists_lists/solution.ml +++ b/exercises/hferee/11_lists_lists/solution.ml @@ -38,7 +38,7 @@ let rec merge l1 l2 = else x2::(merge l1 l2') -let wrap = List.map (fun x -> [x]) +let wrap a = List.map (fun x -> [x]) a let rec flatten_merge = function | x1::x2::l' -> diff --git a/exercises/hferee/3.0_sudoku/prelude.ml b/exercises/hferee/3.0_sudoku/prelude.ml index 46b81db..08a2a8a 100644 --- a/exercises/hferee/3.0_sudoku/prelude.ml +++ b/exercises/hferee/3.0_sudoku/prelude.ml @@ -27,6 +27,3 @@ let print_grid f (g : grid) : unit = ; print_string "\n" in let a t (x, y, z) = t x, t y, t z in print_grid_aux (a (fun ll -> a (fun (x, _, _) -> x) ll, a (fun (_, x, _) -> x) ll, a (fun (_, _, x) -> x) ll) g);; - - -#install_printer print_grid;; diff --git a/exercises/smelodesousa/F4/4-fun-1/test.ml b/exercises/smelodesousa/F4/4-fun-1/test.ml index fc990fc..d81f89d 100644 --- a/exercises/smelodesousa/F4/4-fun-1/test.ml +++ b/exercises/smelodesousa/F4/4-fun-1/test.ml @@ -186,7 +186,7 @@ type ('a, 'b, 'c) correct1_q11 = ('a -> 'b) -> ('b list -> 'c) -> 'a list -> 'c type ('a, 'b, 'c) correct2_q11 = (int -> float) -> (float list -> string) -> int list -> string type ('a, 'b, 'c) correct3_q11 = (bool -> char) -> (char list -> int) -> bool list -> int type ('a, 'b, 'c) incorrect1_q11 = ('a -> 'b) -> (int -> 'c) -> 'a list -> 'c -type ('a, 'b, 'c) incorrect1_q11 = ('a -> 'b) -> ('b list -> 'c) -> float -> 'c +type ('a, 'b, 'c) incorrect2_q11 = ('a -> 'b) -> ('b list -> 'c) -> float -> 'c let ex11 = diff --git a/exercises/smelodesousa/F5/5-lists/test.ml b/exercises/smelodesousa/F5/5-lists/test.ml index a392c2b..3ae2e2a 100644 --- a/exercises/smelodesousa/F5/5-lists/test.ml +++ b/exercises/smelodesousa/F5/5-lists/test.ml @@ -2,7 +2,7 @@ open Test_lib open Report (* samplers *) -let sample_Palindrome () = +let csample_Palindrome () = let () = Random.self_init () in if ((Random.int 2) = 0) then @@ -15,17 +15,17 @@ let sample_Palindrome () = else sample_list ~min_size: 5 ~max_size: 20 (fun () -> ((Random.int 1000) - 500)) () -let sample_allChar () = +let csample_allChar () = let () = Random.self_init () in char_of_int ((Random.int 95) + 32) -let sample_Slist () = +let csample_Slist () = let () = Random.self_init () in if ((Random.int 2) = 1) then let l = (sample_list ~min_size: 5 ~max_size: 20 (fun () -> ((Random.int 100) - 50))) in ((l()) , compare) else let l = (sample_list ~min_size: 5 ~max_size: 20 ~sorted: true (fun () -> ((Random.int 100) - 50))) in ((l()) , compare) -let sample_sorted_dupped_list () = +let csample_sorted_dupped_list () = let s = ref (-1) in let f = ref (-1) in let () = Random.self_init () in @@ -48,7 +48,7 @@ let sample_sorted_dupped_list () = ranged_list !s !f; end -let sample_dupped_list () = +let csample_dupped_list () = let () = Random.self_init () in let size = Random.int(15) + 1 in let rec ranged_list pos size = @@ -86,7 +86,7 @@ let palindromeS = test_function_1_against_solution [%ty: int list -> bool] "palindrome" - ~sampler: sample_Palindrome + ~sampler: csample_Palindrome ~gen: 6 [([]); [1]; [2; 2]; [2; 1; 2]]) @@ -96,7 +96,7 @@ let uppercaseS = test_function_1_against_solution [%ty: char list -> char list] "uppercase" - ~sampler: (sample_list ~min_size: 5 ~max_size: 20 sample_allChar) + ~sampler: (sample_list ~min_size: 5 ~max_size: 20 csample_allChar) ~gen: 7 [([]); ['a']; ['%']]) @@ -106,7 +106,7 @@ let is_sortedS = test_function_2_against_solution [%ty: int list -> (int -> int -> int) -> bool] "is_sorted" - ~sampler: (sample_Slist) + ~sampler: (csample_Slist) ~gen: 8 [([], compare); ([-1;0;0;1], compare)]) @@ -116,7 +116,7 @@ let remove_sorted_dupsS = test_function_1_against_solution [%ty: int list -> int list] "remove_duplicate_sorted" - ~sampler: sample_sorted_dupped_list + ~sampler: csample_sorted_dupped_list ~gen: 8 [([]); ([-1;0;0;1])]) @@ -126,7 +126,7 @@ let remove_dupsS = test_function_1_against_solution [%ty: int list -> int list] "remove_duplicate" - ~sampler: sample_dupped_list + ~sampler: csample_dupped_list ~gen: 8 [([]); ([-1;0;0;1])]) From b6e1f61f1956173d08051dbd85c68f98de60c08d Mon Sep 17 00:00:00 2001 From: Louis Gesbert Date: Mon, 30 Oct 2023 14:01:21 +0100 Subject: [PATCH 8/8] Fix random deadlock when grading the mooc FIFO exercise --- exercises/mooc/week3/seq1/ex1/solution.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/exercises/mooc/week3/seq1/ex1/solution.ml b/exercises/mooc/week3/seq1/ex1/solution.ml index 2a007d7..88822f1 100644 --- a/exercises/mooc/week3/seq1/ex1/solution.ml +++ b/exercises/mooc/week3/seq1/ex1/solution.ml @@ -17,4 +17,7 @@ let split l = let rec dequeue (front, back) = match front with | x :: front' -> (x, (front', back)) - | [] -> assert (back <> []); dequeue (split back) + | [] -> match back with + | [] -> assert false + | [x] -> (x, ([], [])) + | back -> dequeue (split back)