trackler 2.2.1.16 → 2.2.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/tracks/delphi/config/maintainers.json +1 -1
- data/tracks/objective-c/docs/SNIPPET.txt +10 -0
- data/tracks/python/.gitignore +1 -0
- data/tracks/python/exercises/accumulate/accumulate.py +1 -1
- data/tracks/python/exercises/allergies/allergies.py +9 -1
- data/tracks/python/exercises/alphametics/alphametics.py +1 -1
- data/tracks/python/exercises/anagram/anagram.py +1 -1
- data/tracks/python/exercises/atbash-cipher/atbash_cipher.py +2 -2
- data/tracks/python/exercises/beer-song/beer_song.py +2 -2
- data/tracks/python/exercises/binary/binary.py +1 -1
- data/tracks/python/exercises/binary-search/binary_search.py +1 -1
- data/tracks/python/exercises/bob/bob.py +1 -1
- data/tracks/python/exercises/book-store/book_store.py +1 -1
- data/tracks/python/exercises/bracket-push/bracket_push.py +1 -1
- data/tracks/python/requirements-travis.txt +1 -1
- data/tracks/rust/config.json +21 -7
- data/tracks/rust/exercises/crypto-square/.gitignore +7 -0
- data/tracks/rust/exercises/crypto-square/Cargo-example.toml +7 -0
- data/tracks/rust/exercises/crypto-square/Cargo.toml +6 -0
- data/tracks/rust/exercises/crypto-square/README.md +106 -0
- data/tracks/rust/exercises/crypto-square/example.rs +114 -0
- data/tracks/rust/exercises/crypto-square/src/lib.rs +3 -0
- data/tracks/rust/exercises/crypto-square/tests/crypto-square.rs +81 -0
- data/tracks/sml/Makefile +1 -1
- data/tracks/sml/config.json +30 -0
- data/tracks/sml/exercises/atbash-cipher/README.md +64 -0
- data/tracks/sml/exercises/atbash-cipher/atbash-cipher.sml +5 -0
- data/tracks/sml/exercises/atbash-cipher/example.sml +32 -0
- data/tracks/sml/exercises/atbash-cipher/test.sml +52 -0
- data/tracks/sml/exercises/atbash-cipher/testlib.sml +159 -0
- data/tracks/sml/exercises/pangram/README.md +45 -0
- data/tracks/sml/exercises/pangram/example.sml +18 -0
- data/tracks/sml/exercises/pangram/pangram.sml +2 -0
- data/tracks/sml/exercises/pangram/test.sml +41 -0
- data/tracks/sml/exercises/pangram/testlib.sml +159 -0
- data/tracks/sml/exercises/perfect-numbers/README.md +54 -0
- data/tracks/sml/exercises/perfect-numbers/example.sml +26 -0
- data/tracks/sml/exercises/perfect-numbers/perfect-numbers.sml +6 -0
- data/tracks/sml/exercises/perfect-numbers/test.sml +59 -0
- data/tracks/sml/exercises/perfect-numbers/testlib.sml +159 -0
- metadata +24 -1
@@ -0,0 +1,32 @@
|
|
1
|
+
fun chunkify chunkSz s =
|
2
|
+
let
|
3
|
+
val sz = size s
|
4
|
+
|
5
|
+
fun chunker 0 _ = chunker chunkSz (substring (s, 0, chunkSz))
|
6
|
+
| chunker i acc =
|
7
|
+
if sz = i
|
8
|
+
then acc
|
9
|
+
else if sz - i < chunkSz
|
10
|
+
then acc ^ " " ^ substring (s, i, size s - i)
|
11
|
+
else chunker (i + chunkSz) (acc ^ " " ^ substring (s, i, chunkSz))
|
12
|
+
in
|
13
|
+
if chunkSz > sz
|
14
|
+
then s
|
15
|
+
else chunker 0 ""
|
16
|
+
end
|
17
|
+
|
18
|
+
fun cipher c =
|
19
|
+
let
|
20
|
+
open Char
|
21
|
+
val n = ord #"z" + ord #"a"
|
22
|
+
val c = toLower c
|
23
|
+
in
|
24
|
+
if isAlpha c
|
25
|
+
then (toString o chr) (n - ord c)
|
26
|
+
else if isDigit c
|
27
|
+
then toString c
|
28
|
+
else ""
|
29
|
+
end
|
30
|
+
|
31
|
+
val decode = String.translate cipher
|
32
|
+
val encode = (chunkify 5) o decode
|
@@ -0,0 +1,52 @@
|
|
1
|
+
(* version 1.0.0 *)
|
2
|
+
|
3
|
+
use "atbash-cipher.sml";
|
4
|
+
use "testlib.sml";
|
5
|
+
|
6
|
+
infixr |>
|
7
|
+
fun x |> f = f x
|
8
|
+
|
9
|
+
val testsuite =
|
10
|
+
describe "atbash-cipher" [
|
11
|
+
describe "encode" [
|
12
|
+
test "encode yes"
|
13
|
+
(fn _ => encode ("yes") |> Expect.equalTo "bvh"),
|
14
|
+
|
15
|
+
test "encode no"
|
16
|
+
(fn _ => encode ("no") |> Expect.equalTo "ml"),
|
17
|
+
|
18
|
+
test "encode OMG"
|
19
|
+
(fn _ => encode ("OMG") |> Expect.equalTo "lnt"),
|
20
|
+
|
21
|
+
test "encode spaces"
|
22
|
+
(fn _ => encode ("O M G") |> Expect.equalTo "lnt"),
|
23
|
+
|
24
|
+
test "encode mindblowingly"
|
25
|
+
(fn _ => encode ("mindblowingly") |> Expect.equalTo "nrmwy oldrm tob"),
|
26
|
+
|
27
|
+
test "encode numbers"
|
28
|
+
(fn _ => encode ("Testing,1 2 3, testing.") |> Expect.equalTo "gvhgr mt123 gvhgr mt"),
|
29
|
+
|
30
|
+
test "encode deep thought"
|
31
|
+
(fn _ => encode ("Truth is fiction.") |> Expect.equalTo "gifgs rhurx grlm"),
|
32
|
+
|
33
|
+
test "encode all the letters"
|
34
|
+
(fn _ => encode ("The quick brown fox jumps over the lazy dog.") |> Expect.equalTo "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt")
|
35
|
+
],
|
36
|
+
|
37
|
+
describe "decode" [
|
38
|
+
test "decode exercism"
|
39
|
+
(fn _ => decode ("vcvix rhn") |> Expect.equalTo "exercism"),
|
40
|
+
|
41
|
+
test "decode a sentence"
|
42
|
+
(fn _ => decode ("zmlyh gzxov rhlug vmzhg vkkrm thglm v") |> Expect.equalTo "anobstacleisoftenasteppingstone"),
|
43
|
+
|
44
|
+
test "decode numbers"
|
45
|
+
(fn _ => decode ("gvhgr mt123 gvhgr mt") |> Expect.equalTo "testing123testing"),
|
46
|
+
|
47
|
+
test "decode all the letters"
|
48
|
+
(fn _ => decode ("gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt") |> Expect.equalTo "thequickbrownfoxjumpsoverthelazydog")
|
49
|
+
]
|
50
|
+
]
|
51
|
+
|
52
|
+
val _ = Test.run testsuite
|
@@ -0,0 +1,159 @@
|
|
1
|
+
structure Expect =
|
2
|
+
struct
|
3
|
+
datatype expectation = Pass | Fail of string * string
|
4
|
+
|
5
|
+
local
|
6
|
+
fun failEq b a =
|
7
|
+
Fail ("Expected: " ^ b, "Got: " ^ a)
|
8
|
+
|
9
|
+
fun failExn b a =
|
10
|
+
Fail ("Expected: " ^ b, "Raised: " ^ a)
|
11
|
+
|
12
|
+
fun exnName (e: exn): string = General.exnName e
|
13
|
+
in
|
14
|
+
fun truthy a =
|
15
|
+
if a
|
16
|
+
then Pass
|
17
|
+
else failEq "true" "false"
|
18
|
+
|
19
|
+
fun falsy a =
|
20
|
+
if a
|
21
|
+
then failEq "false" "true"
|
22
|
+
else Pass
|
23
|
+
|
24
|
+
fun equalTo b a =
|
25
|
+
if a = b
|
26
|
+
then Pass
|
27
|
+
else failEq (PolyML.makestring b) (PolyML.makestring a)
|
28
|
+
|
29
|
+
fun nearTo b a =
|
30
|
+
if Real.== (a, b)
|
31
|
+
then Pass
|
32
|
+
else failEq (Real.toString b) (Real.toString a)
|
33
|
+
|
34
|
+
fun anyError f =
|
35
|
+
(
|
36
|
+
f ();
|
37
|
+
failExn "an exception" "Nothing"
|
38
|
+
) handle _ => Pass
|
39
|
+
|
40
|
+
fun error e f =
|
41
|
+
(
|
42
|
+
f ();
|
43
|
+
failExn (exnName e) "Nothing"
|
44
|
+
) handle e' => if exnMessage e' = exnMessage e
|
45
|
+
then Pass
|
46
|
+
else failExn (exnMessage e) (exnMessage e')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
structure TermColor =
|
51
|
+
struct
|
52
|
+
datatype color = Red | Green | Yellow | Normal
|
53
|
+
|
54
|
+
fun f Red = "\027[31m"
|
55
|
+
| f Green = "\027[32m"
|
56
|
+
| f Yellow = "\027[33m"
|
57
|
+
| f Normal = "\027[0m"
|
58
|
+
|
59
|
+
fun colorize color s = (f color) ^ s ^ (f Normal)
|
60
|
+
|
61
|
+
val redit = colorize Red
|
62
|
+
|
63
|
+
val greenit = colorize Green
|
64
|
+
|
65
|
+
val yellowit = colorize Yellow
|
66
|
+
end
|
67
|
+
|
68
|
+
structure Test =
|
69
|
+
struct
|
70
|
+
datatype testnode = TestGroup of string * testnode list
|
71
|
+
| Test of string * (unit -> Expect.expectation)
|
72
|
+
|
73
|
+
local
|
74
|
+
datatype evaluation = Success of string
|
75
|
+
| Failure of string * string * string
|
76
|
+
| Error of string * string
|
77
|
+
|
78
|
+
fun indent n s = (implode (List.tabulate (n, fn _ => #" "))) ^ s
|
79
|
+
|
80
|
+
fun fmt indentlvl ev =
|
81
|
+
let
|
82
|
+
val check = TermColor.greenit "\226\156\148 " (* ✔ *)
|
83
|
+
val cross = TermColor.redit "\226\156\150 " (* ✖ *)
|
84
|
+
val indentlvl = indentlvl * 2
|
85
|
+
in
|
86
|
+
case ev of
|
87
|
+
Success descr => indent indentlvl (check ^ descr)
|
88
|
+
| Failure (descr, exp, got) =>
|
89
|
+
String.concatWith "\n" [indent indentlvl (cross ^ descr),
|
90
|
+
indent (indentlvl + 2) exp,
|
91
|
+
indent (indentlvl + 2) got]
|
92
|
+
| Error (descr, reason) =>
|
93
|
+
String.concatWith "\n" [indent indentlvl (cross ^ descr),
|
94
|
+
indent (indentlvl + 2) (TermColor.redit reason)]
|
95
|
+
end
|
96
|
+
|
97
|
+
fun eval (TestGroup _) = raise Fail "Only a 'Test' can be evaluated"
|
98
|
+
| eval (Test (descr, thunk)) =
|
99
|
+
(
|
100
|
+
case thunk () of
|
101
|
+
Expect.Pass => ((1, 0, 0), Success descr)
|
102
|
+
| Expect.Fail (s, s') => ((0, 1, 0), Failure (descr, s, s'))
|
103
|
+
)
|
104
|
+
handle e => ((0, 0, 1), Error (descr, "Unexpected error: " ^ exnMessage e))
|
105
|
+
|
106
|
+
fun flatten depth testnode =
|
107
|
+
let
|
108
|
+
fun sum (x, y, z) (a, b, c) = (x + a, y + b, z + c)
|
109
|
+
|
110
|
+
fun aux (t, (counter, acc)) =
|
111
|
+
let
|
112
|
+
val (counter', texts) = flatten (depth + 1) t
|
113
|
+
in
|
114
|
+
(sum counter' counter, texts :: acc)
|
115
|
+
end
|
116
|
+
in
|
117
|
+
case testnode of
|
118
|
+
TestGroup (descr, ts) =>
|
119
|
+
let
|
120
|
+
val (counter, texts) = foldr aux ((0, 0, 0), []) ts
|
121
|
+
in
|
122
|
+
(counter, (indent (depth * 2) descr) :: List.concat texts)
|
123
|
+
end
|
124
|
+
| Test _ =>
|
125
|
+
let
|
126
|
+
val (counter, evaluation) = eval testnode
|
127
|
+
in
|
128
|
+
(counter, [fmt depth evaluation])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
fun println s = print (s ^ "\n")
|
133
|
+
in
|
134
|
+
fun run suite =
|
135
|
+
let
|
136
|
+
val ((succeeded, failed, errored), texts) = flatten 0 suite
|
137
|
+
|
138
|
+
val summary = String.concatWith ", " [
|
139
|
+
TermColor.greenit ((Int.toString succeeded) ^ " passed"),
|
140
|
+
TermColor.redit ((Int.toString failed) ^ " failed"),
|
141
|
+
TermColor.redit ((Int.toString errored) ^ " errored"),
|
142
|
+
(Int.toString (succeeded + failed + errored)) ^ " total"
|
143
|
+
]
|
144
|
+
|
145
|
+
val status = if failed = 0 andalso errored = 0
|
146
|
+
then OS.Process.success
|
147
|
+
else OS.Process.failure
|
148
|
+
|
149
|
+
in
|
150
|
+
List.app println texts;
|
151
|
+
println "";
|
152
|
+
println ("Tests: " ^ summary);
|
153
|
+
OS.Process.exit status
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
fun describe description tests = Test.TestGroup (description, tests)
|
159
|
+
fun test description thunk = Test.Test (description, thunk)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Pangram
|
2
|
+
|
3
|
+
Determine if a sentence is a pangram. A pangram (Greek: παν γράμμα, pan gramma,
|
4
|
+
"every letter") is a sentence using every letter of the alphabet at least once.
|
5
|
+
The best known English pangram is:
|
6
|
+
> The quick brown fox jumps over the lazy dog.
|
7
|
+
|
8
|
+
The alphabet used consists of ASCII letters `a` to `z`, inclusive, and is case
|
9
|
+
insensitive. Input will not contain non-ASCII symbols.
|
10
|
+
|
11
|
+
## Loading your exercise implementation in PolyML
|
12
|
+
|
13
|
+
```
|
14
|
+
$ poly --use {exercise}.sml
|
15
|
+
```
|
16
|
+
|
17
|
+
Or:
|
18
|
+
|
19
|
+
```
|
20
|
+
$ poly
|
21
|
+
> use "{exercise}.sml";
|
22
|
+
```
|
23
|
+
|
24
|
+
**Note:** You have to replace {exercise}.
|
25
|
+
|
26
|
+
## Running the tests
|
27
|
+
|
28
|
+
```
|
29
|
+
$ poly -q --use test.sml
|
30
|
+
```
|
31
|
+
|
32
|
+
## Feedback, Issues, Pull Requests
|
33
|
+
|
34
|
+
The [exercism/sml](https://github.com/exercism/sml) repository on
|
35
|
+
GitHub is the home for all of the Standard ML exercises.
|
36
|
+
|
37
|
+
If you have feedback about an exercise, or want to help implementing a new
|
38
|
+
one, head over there and create an issue. We'll do our best to help you!
|
39
|
+
|
40
|
+
## Source
|
41
|
+
|
42
|
+
Wikipedia [https://en.wikipedia.org/wiki/Pangram](https://en.wikipedia.org/wiki/Pangram)
|
43
|
+
|
44
|
+
## Submitting Incomplete Solutions
|
45
|
+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
fun isPangram (input: string): bool =
|
2
|
+
let
|
3
|
+
val counter = Array.tabulate (26, fn _ => 0)
|
4
|
+
val chars = map Char.toLower (String.explode input)
|
5
|
+
val aCode = ord #"a"
|
6
|
+
|
7
|
+
fun updateCounter c =
|
8
|
+
let
|
9
|
+
val index = ord c - aCode
|
10
|
+
in
|
11
|
+
if index < 0
|
12
|
+
then ()
|
13
|
+
else Array.update (counter, index, (Array.sub (counter, index)) + 1)
|
14
|
+
end
|
15
|
+
in
|
16
|
+
List.app updateCounter chars;
|
17
|
+
Array.all (fn x => x > 0) counter
|
18
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
(* version 1.1.0 *)
|
2
|
+
|
3
|
+
use "pangram.sml";
|
4
|
+
use "testlib.sml";
|
5
|
+
|
6
|
+
infixr |>
|
7
|
+
fun x |> f = f x
|
8
|
+
|
9
|
+
val testsuite =
|
10
|
+
describe "pangram" [
|
11
|
+
describe "Check if the given string is an pangram" [
|
12
|
+
test "sentence empty"
|
13
|
+
(fn _ => isPangram ("") |> Expect.falsy),
|
14
|
+
|
15
|
+
test "pangram with only lower case"
|
16
|
+
(fn _ => isPangram ("the quick brown fox jumps over the lazy dog") |> Expect.truthy),
|
17
|
+
|
18
|
+
test "missing character 'x'"
|
19
|
+
(fn _ => isPangram ("a quick movement of the enemy will jeopardize five gunboats") |> Expect.falsy),
|
20
|
+
|
21
|
+
test "another missing character 'x'"
|
22
|
+
(fn _ => isPangram ("the quick brown fish jumps over the lazy dog") |> Expect.falsy),
|
23
|
+
|
24
|
+
test "pangram with underscores"
|
25
|
+
(fn _ => isPangram ("the_quick_brown_fox_jumps_over_the_lazy_dog") |> Expect.truthy),
|
26
|
+
|
27
|
+
test "pangram with numbers"
|
28
|
+
(fn _ => isPangram ("the 1 quick brown fox jumps over the 2 lazy dogs") |> Expect.truthy),
|
29
|
+
|
30
|
+
test "missing letters replaced by numbers"
|
31
|
+
(fn _ => isPangram ("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog") |> Expect.falsy),
|
32
|
+
|
33
|
+
test "pangram with mixed case and punctuation"
|
34
|
+
(fn _ => isPangram ("\"Five quacking Zephyrs jolt my wax bed.\"") |> Expect.truthy),
|
35
|
+
|
36
|
+
test "upper and lower case versions of the same character should not be counted separately"
|
37
|
+
(fn _ => isPangram ("the quick brown fox jumps over with lazy FX") |> Expect.falsy)
|
38
|
+
]
|
39
|
+
]
|
40
|
+
|
41
|
+
val _ = Test.run testsuite
|
@@ -0,0 +1,159 @@
|
|
1
|
+
structure Expect =
|
2
|
+
struct
|
3
|
+
datatype expectation = Pass | Fail of string * string
|
4
|
+
|
5
|
+
local
|
6
|
+
fun failEq b a =
|
7
|
+
Fail ("Expected: " ^ b, "Got: " ^ a)
|
8
|
+
|
9
|
+
fun failExn b a =
|
10
|
+
Fail ("Expected: " ^ b, "Raised: " ^ a)
|
11
|
+
|
12
|
+
fun exnName (e: exn): string = General.exnName e
|
13
|
+
in
|
14
|
+
fun truthy a =
|
15
|
+
if a
|
16
|
+
then Pass
|
17
|
+
else failEq "true" "false"
|
18
|
+
|
19
|
+
fun falsy a =
|
20
|
+
if a
|
21
|
+
then failEq "false" "true"
|
22
|
+
else Pass
|
23
|
+
|
24
|
+
fun equalTo b a =
|
25
|
+
if a = b
|
26
|
+
then Pass
|
27
|
+
else failEq (PolyML.makestring b) (PolyML.makestring a)
|
28
|
+
|
29
|
+
fun nearTo b a =
|
30
|
+
if Real.== (a, b)
|
31
|
+
then Pass
|
32
|
+
else failEq (Real.toString b) (Real.toString a)
|
33
|
+
|
34
|
+
fun anyError f =
|
35
|
+
(
|
36
|
+
f ();
|
37
|
+
failExn "an exception" "Nothing"
|
38
|
+
) handle _ => Pass
|
39
|
+
|
40
|
+
fun error e f =
|
41
|
+
(
|
42
|
+
f ();
|
43
|
+
failExn (exnName e) "Nothing"
|
44
|
+
) handle e' => if exnMessage e' = exnMessage e
|
45
|
+
then Pass
|
46
|
+
else failExn (exnMessage e) (exnMessage e')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
structure TermColor =
|
51
|
+
struct
|
52
|
+
datatype color = Red | Green | Yellow | Normal
|
53
|
+
|
54
|
+
fun f Red = "\027[31m"
|
55
|
+
| f Green = "\027[32m"
|
56
|
+
| f Yellow = "\027[33m"
|
57
|
+
| f Normal = "\027[0m"
|
58
|
+
|
59
|
+
fun colorize color s = (f color) ^ s ^ (f Normal)
|
60
|
+
|
61
|
+
val redit = colorize Red
|
62
|
+
|
63
|
+
val greenit = colorize Green
|
64
|
+
|
65
|
+
val yellowit = colorize Yellow
|
66
|
+
end
|
67
|
+
|
68
|
+
structure Test =
|
69
|
+
struct
|
70
|
+
datatype testnode = TestGroup of string * testnode list
|
71
|
+
| Test of string * (unit -> Expect.expectation)
|
72
|
+
|
73
|
+
local
|
74
|
+
datatype evaluation = Success of string
|
75
|
+
| Failure of string * string * string
|
76
|
+
| Error of string * string
|
77
|
+
|
78
|
+
fun indent n s = (implode (List.tabulate (n, fn _ => #" "))) ^ s
|
79
|
+
|
80
|
+
fun fmt indentlvl ev =
|
81
|
+
let
|
82
|
+
val check = TermColor.greenit "\226\156\148 " (* ✔ *)
|
83
|
+
val cross = TermColor.redit "\226\156\150 " (* ✖ *)
|
84
|
+
val indentlvl = indentlvl * 2
|
85
|
+
in
|
86
|
+
case ev of
|
87
|
+
Success descr => indent indentlvl (check ^ descr)
|
88
|
+
| Failure (descr, exp, got) =>
|
89
|
+
String.concatWith "\n" [indent indentlvl (cross ^ descr),
|
90
|
+
indent (indentlvl + 2) exp,
|
91
|
+
indent (indentlvl + 2) got]
|
92
|
+
| Error (descr, reason) =>
|
93
|
+
String.concatWith "\n" [indent indentlvl (cross ^ descr),
|
94
|
+
indent (indentlvl + 2) (TermColor.redit reason)]
|
95
|
+
end
|
96
|
+
|
97
|
+
fun eval (TestGroup _) = raise Fail "Only a 'Test' can be evaluated"
|
98
|
+
| eval (Test (descr, thunk)) =
|
99
|
+
(
|
100
|
+
case thunk () of
|
101
|
+
Expect.Pass => ((1, 0, 0), Success descr)
|
102
|
+
| Expect.Fail (s, s') => ((0, 1, 0), Failure (descr, s, s'))
|
103
|
+
)
|
104
|
+
handle e => ((0, 0, 1), Error (descr, "Unexpected error: " ^ exnMessage e))
|
105
|
+
|
106
|
+
fun flatten depth testnode =
|
107
|
+
let
|
108
|
+
fun sum (x, y, z) (a, b, c) = (x + a, y + b, z + c)
|
109
|
+
|
110
|
+
fun aux (t, (counter, acc)) =
|
111
|
+
let
|
112
|
+
val (counter', texts) = flatten (depth + 1) t
|
113
|
+
in
|
114
|
+
(sum counter' counter, texts :: acc)
|
115
|
+
end
|
116
|
+
in
|
117
|
+
case testnode of
|
118
|
+
TestGroup (descr, ts) =>
|
119
|
+
let
|
120
|
+
val (counter, texts) = foldr aux ((0, 0, 0), []) ts
|
121
|
+
in
|
122
|
+
(counter, (indent (depth * 2) descr) :: List.concat texts)
|
123
|
+
end
|
124
|
+
| Test _ =>
|
125
|
+
let
|
126
|
+
val (counter, evaluation) = eval testnode
|
127
|
+
in
|
128
|
+
(counter, [fmt depth evaluation])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
fun println s = print (s ^ "\n")
|
133
|
+
in
|
134
|
+
fun run suite =
|
135
|
+
let
|
136
|
+
val ((succeeded, failed, errored), texts) = flatten 0 suite
|
137
|
+
|
138
|
+
val summary = String.concatWith ", " [
|
139
|
+
TermColor.greenit ((Int.toString succeeded) ^ " passed"),
|
140
|
+
TermColor.redit ((Int.toString failed) ^ " failed"),
|
141
|
+
TermColor.redit ((Int.toString errored) ^ " errored"),
|
142
|
+
(Int.toString (succeeded + failed + errored)) ^ " total"
|
143
|
+
]
|
144
|
+
|
145
|
+
val status = if failed = 0 andalso errored = 0
|
146
|
+
then OS.Process.success
|
147
|
+
else OS.Process.failure
|
148
|
+
|
149
|
+
in
|
150
|
+
List.app println texts;
|
151
|
+
println "";
|
152
|
+
println ("Tests: " ^ summary);
|
153
|
+
OS.Process.exit status
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
fun describe description tests = Test.TestGroup (description, tests)
|
159
|
+
fun test description thunk = Test.Test (description, thunk)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Perfect Numbers
|
2
|
+
|
3
|
+
Determine if a number is perfect, abundant, or deficient based on
|
4
|
+
Nicomachus' (60 - 120 CE) classification scheme for natural numbers.
|
5
|
+
|
6
|
+
The Greek mathematician [Nicomachus](https://en.wikipedia.org/wiki/Nicomachus) devised a classification scheme for natural numbers, identifying each as belonging uniquely to the categories of **perfect**, **abundant**, or **deficient** based on their [aliquot sum](https://en.wikipedia.org/wiki/Aliquot_sum). The aliquot sum is defined as the sum of the factors of a number not including the number itself. For example, the aliquot sum of 15 is (1 + 3 + 5) = 9
|
7
|
+
|
8
|
+
- **Perfect**: aliquot sum = number
|
9
|
+
- 6 is a perfect number because (1 + 2 + 3) = 6
|
10
|
+
- 28 is a perfect number because (1 + 2 + 4 + 7 + 14) = 28
|
11
|
+
- **Abundant**: aliquot sum > number
|
12
|
+
- 12 is an abundant number because (1 + 2 + 3 + 4 + 6) = 16
|
13
|
+
- 24 is an abundant number because (1 + 2 + 3 + 4 + 6 + 8 + 12) = 36
|
14
|
+
- **Deficient**: aliquot sum < number
|
15
|
+
- 8 is a deficient number because (1 + 2 + 4) = 7
|
16
|
+
- Prime numbers are deficient
|
17
|
+
|
18
|
+
Implement the `classify` function, it should return one of `Abundant`, `Deficient` or `Perfect`. If the input param is not a positive integer, raise the exception `NotAPositiveInteger`.
|
19
|
+
|
20
|
+
## Loading your exercise implementation in PolyML
|
21
|
+
|
22
|
+
```
|
23
|
+
$ poly --use {exercise}.sml
|
24
|
+
```
|
25
|
+
|
26
|
+
Or:
|
27
|
+
|
28
|
+
```
|
29
|
+
$ poly
|
30
|
+
> use "{exercise}.sml";
|
31
|
+
```
|
32
|
+
|
33
|
+
**Note:** You have to replace {exercise}.
|
34
|
+
|
35
|
+
## Running the tests
|
36
|
+
|
37
|
+
```
|
38
|
+
$ poly -q --use test.sml
|
39
|
+
```
|
40
|
+
|
41
|
+
## Feedback, Issues, Pull Requests
|
42
|
+
|
43
|
+
The [exercism/sml](https://github.com/exercism/sml) repository on
|
44
|
+
GitHub is the home for all of the Standard ML exercises.
|
45
|
+
|
46
|
+
If you have feedback about an exercise, or want to help implementing a new
|
47
|
+
one, head over there and create an issue. We'll do our best to help you!
|
48
|
+
|
49
|
+
## Source
|
50
|
+
|
51
|
+
Taken from Chapter 2 of Functional Thinking by Neal Ford. [http://shop.oreilly.com/product/0636920029687.do](http://shop.oreilly.com/product/0636920029687.do)
|
52
|
+
|
53
|
+
## Submitting Incomplete Solutions
|
54
|
+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
@@ -0,0 +1,26 @@
|
|
1
|
+
exception NotAPositiveInteger
|
2
|
+
|
3
|
+
datatype classification = Abundant | Deficient | Perfect
|
4
|
+
|
5
|
+
fun sigma n =
|
6
|
+
let
|
7
|
+
fun loop i =
|
8
|
+
if i * i > n
|
9
|
+
then 0
|
10
|
+
else if n mod i <> 0
|
11
|
+
then loop (i + 1)
|
12
|
+
else if n div i = i
|
13
|
+
then i + loop (i + 1)
|
14
|
+
else i + (n div i) + loop (i + 1)
|
15
|
+
in
|
16
|
+
loop 1
|
17
|
+
end
|
18
|
+
|
19
|
+
fun classify n =
|
20
|
+
if n < 1
|
21
|
+
then raise NotAPositiveInteger
|
22
|
+
else
|
23
|
+
case Int.compare (sigma n, 2 * n) of
|
24
|
+
GREATER => Abundant
|
25
|
+
| LESS => Deficient
|
26
|
+
| _ => Perfect
|
@@ -0,0 +1,59 @@
|
|
1
|
+
(* version 1.0.1 *)
|
2
|
+
|
3
|
+
use "perfect-numbers.sml";
|
4
|
+
use "testlib.sml";
|
5
|
+
|
6
|
+
infixr |>
|
7
|
+
fun x |> f = f x
|
8
|
+
|
9
|
+
val testsuite =
|
10
|
+
describe "perfect-numbers" [
|
11
|
+
describe "Perfect numbers" [
|
12
|
+
test "Smallest perfect number is classified correctly"
|
13
|
+
(fn _ => classify (6) |> Expect.equalTo Perfect),
|
14
|
+
|
15
|
+
test "Medium perfect number is classified correctly"
|
16
|
+
(fn _ => classify (28) |> Expect.equalTo Perfect),
|
17
|
+
|
18
|
+
test "Large perfect number is classified correctly"
|
19
|
+
(fn _ => classify (33550336) |> Expect.equalTo Perfect)
|
20
|
+
],
|
21
|
+
|
22
|
+
describe "Abundant numbers" [
|
23
|
+
test "Smallest abundant number is classified correctly"
|
24
|
+
(fn _ => classify (12) |> Expect.equalTo Abundant),
|
25
|
+
|
26
|
+
test "Medium abundant number is classified correctly"
|
27
|
+
(fn _ => classify (30) |> Expect.equalTo Abundant),
|
28
|
+
|
29
|
+
test "Large abundant number is classified correctly"
|
30
|
+
(fn _ => classify (33550335) |> Expect.equalTo Abundant)
|
31
|
+
],
|
32
|
+
|
33
|
+
describe "Deficient numbers" [
|
34
|
+
test "Smallest prime deficient number is classified correctly"
|
35
|
+
(fn _ => classify (2) |> Expect.equalTo Deficient),
|
36
|
+
|
37
|
+
test "Smallest non-prime deficient number is classified correctly"
|
38
|
+
(fn _ => classify (4) |> Expect.equalTo Deficient),
|
39
|
+
|
40
|
+
test "Medium deficient number is classified correctly"
|
41
|
+
(fn _ => classify (32) |> Expect.equalTo Deficient),
|
42
|
+
|
43
|
+
test "Large deficient number is classified correctly"
|
44
|
+
(fn _ => classify (33550337) |> Expect.equalTo Deficient),
|
45
|
+
|
46
|
+
test "Edge case (no factors other than itself) is classified correctly"
|
47
|
+
(fn _ => classify (1) |> Expect.equalTo Deficient)
|
48
|
+
],
|
49
|
+
|
50
|
+
describe "Invalid inputs" [
|
51
|
+
test "Zero is rejected (not a natural number)"
|
52
|
+
(fn _ => (fn _ => classify (~1)) |> Expect.error NotAPositiveInteger),
|
53
|
+
|
54
|
+
test "Negative integer is rejected (not a natural number)"
|
55
|
+
(fn _ => (fn _ => classify (~1)) |> Expect.error NotAPositiveInteger)
|
56
|
+
]
|
57
|
+
]
|
58
|
+
|
59
|
+
val _ = Test.run testsuite
|