trackler 2.2.1.16 → 2.2.1.17
Sign up to get free protection for your applications and to get access to all the features.
- 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
|