trackler 2.2.1.67 → 2.2.1.68

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/README.md +6 -6
  4. data/tracks/csharp/exercises/all-your-base/AllYourBaseTest.cs +9 -5
  5. data/tracks/csharp/exercises/all-your-base/Example.cs +7 -3
  6. data/tracks/csharp/exercises/nth-prime/NthPrimeTest.cs +1 -1
  7. data/tracks/csharp/generators/Exercises/AllYourBase.cs +2 -1
  8. data/tracks/csharp/generators/Exercises/NthPrime.cs +2 -1
  9. data/tracks/fsharp/config.json +11 -0
  10. data/tracks/fsharp/exercises/Exercises.sln +6 -0
  11. data/tracks/fsharp/exercises/isbn-verifier/Example.fs +19 -0
  12. data/tracks/fsharp/exercises/isbn-verifier/IsbnVerifier.fs +3 -0
  13. data/tracks/fsharp/exercises/isbn-verifier/IsbnVerifier.fsproj +23 -0
  14. data/tracks/fsharp/exercises/isbn-verifier/IsbnVerifierTest.fs +61 -0
  15. data/tracks/fsharp/exercises/isbn-verifier/Program.fs +1 -0
  16. data/tracks/fsharp/exercises/isbn-verifier/README.md +32 -0
  17. data/tracks/fsharp/generators/Generators.fs +3 -0
  18. data/tracks/go/exercises/sieve/.meta/gen.go +46 -0
  19. data/tracks/go/exercises/sieve/cases_test.go +37 -0
  20. data/tracks/go/exercises/sieve/example.go +5 -5
  21. data/tracks/go/exercises/sieve/sieve_test.go +12 -21
  22. data/tracks/java/exercises/anagram/src/test/java/AnagramTest.java +7 -6
  23. data/tracks/objective-c/config.json +13 -0
  24. data/tracks/objective-c/exercises/say/SayExample.h +7 -0
  25. data/tracks/objective-c/exercises/say/SayExample.m +55 -0
  26. data/tracks/objective-c/exercises/say/SayTest.m +75 -0
  27. data/tracks/objective-c/xcodeProject/ObjectiveC.xcodeproj/project.pbxproj +18 -0
  28. data/tracks/ocaml/exercises/grade-school/README.md +1 -14
  29. data/tracks/ocaml/exercises/grade-school/example.ml +1 -1
  30. data/tracks/ocaml/exercises/grade-school/grade_school.mli +1 -2
  31. data/tracks/ocaml/exercises/grade-school/test.ml +15 -25
  32. data/tracks/swift/config.json +13 -0
  33. data/tracks/swift/exercises/say/Package.swift +5 -0
  34. data/tracks/swift/exercises/say/README.md +77 -0
  35. data/tracks/swift/exercises/say/Sources/Say.swift +1 -0
  36. data/tracks/swift/exercises/say/Sources/SayExample.swift +48 -0
  37. data/tracks/swift/exercises/say/Tests/LinuxMain.swift +6 -0
  38. data/tracks/swift/exercises/say/Tests/SayTests/SayTests.swift +85 -0
  39. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 956bc9de5fbd416f23d6f7cfeabbc4c28edcd559
4
- data.tar.gz: 9c7a904426ed89d05476a3066a5110c41f694584
3
+ metadata.gz: 93727b2027f0b9bb82b47a47508ca7ffcc6ff35d
4
+ data.tar.gz: 5b5ad9daef87088409e3e8b2c22ad16803cddd8d
5
5
  SHA512:
6
- metadata.gz: bbc5d5a656753aea898eebf5caee55446ad0793cd462e43356369400c83bcfbaf0e9fbc4d990a79569b1b981b9db846a786eebde7ea77e2add4704d8c046f162
7
- data.tar.gz: 4648f51dbfef1b5446d9761608a0f03a195f2026e24a507028d46ca4b2261b959c21f498faf1c8a48419392a60239f1acf924eccac42d070a453bd19fb9433a6
6
+ metadata.gz: 76aff154bb6cfb996c616f4f77cfd2de03d6029fbf1cab3255bb162310a6b41767576a818b001abeec9f3e875b27bc9118833ba83ae8e65a5ed35df9427be62e
7
+ data.tar.gz: 361c13c1a88ce84f99c1bfea50d5af3590fc9ee21a85a22e10c3333089f29cba08211a6e703c6eb1d23c5185636170fd7c93603f4ef6b889e28261121e249f6c
@@ -1,3 +1,3 @@
1
1
  module Trackler
2
- VERSION = "2.2.1.67"
2
+ VERSION = "2.2.1.68"
3
3
  end
@@ -30,13 +30,13 @@ There are three metadata files:
30
30
 
31
31
  * `description.md` - the basic problem description
32
32
  * `metadata.yml` - additional information about the problem, such as where it came from
33
- * `canonical-data.json` (optional) - standardized test inputs and outputs that can be used to implement the problem
33
+ * `canonical-data.json` - standardized test inputs and outputs that can be used to implement the problem
34
34
 
35
35
  ## Test Data Format (canonical-data.json)
36
36
 
37
37
  This data can be incorporated into test programs manually or extracted by a
38
38
  program. The file format is described in `canonical-schema.json`, but it
39
- is easier to understand with a example:
39
+ is easier to understand with an example:
40
40
 
41
41
  ```json
42
42
  { "exercise": "foobar"
@@ -103,11 +103,11 @@ Keep in mind that the description should not simply explain **what** each case
103
103
  is (that is redundant information) but also **why** each case is there. For
104
104
  example, what kinds of implementation mistakes might this case help us find?
105
105
 
106
- There are also some convention about `expected` that you must follow:
106
+ There are also some conventions that must be followed:
107
107
 
108
108
  - All keys should follow the [lowerCamelCase](http://wiki.c2.com/?LowerCamelCase) convention.
109
- - if the input is valid but there is no result for the input, the value at `"expected"` should be `null`.
110
- - if an error is expected (because the input is invalid, or any other reason), the value at `"expected"` should be an object containing exactly one property, `"error"`, whose value is a string.
109
+ - If the input is valid but there is no result for the input, the value at `"expected"` should be `null`.
110
+ - If an error is expected (because the input is invalid, or any other reason), the value at `"expected"` should be an object containing exactly one property, `"error"`, whose value is a string.
111
111
  - The string should explain why the error would occur.
112
112
  - A particular track's implementation of the exercise **need not** necessarily check that the error includes that exact string as the cause, depending on what is idiomatic in the language (it may not be idiomatic to check strings for error messages).
113
113
 
@@ -165,7 +165,7 @@ PATCH changes would never break well-designed test generators, because the test
165
165
 
166
166
  ## Automated Tests
167
167
 
168
- `canonical-data.json` for each exercise is checked for compliancy against the [canonical-schema.json](canonical-schema.json).
168
+ `canonical-data.json` for each exercise is checked for compliance against the [canonical-schema.json](canonical-schema.json).
169
169
  In order to run these tests, you will need to have `node` and `npm` installed on your system.
170
170
  Install them from [here](https://nodejs.org/en/). (`npm` comes bundled with most installations of `node`).
171
171
 
@@ -1,4 +1,4 @@
1
- // This file was auto-generated based on version 1.1.0 of the canonical data.
1
+ // This file was auto-generated based on version 2.0.0 of the canonical data.
2
2
 
3
3
  using Xunit;
4
4
  using System;
@@ -91,7 +91,8 @@ public class AllYourBaseTest
91
91
  var inputBase = 2;
92
92
  var inputDigits = new int[0];
93
93
  var outputBase = 10;
94
- Assert.Throws<ArgumentException>(() => AllYourBase.Rebase(inputBase, inputDigits, outputBase));
94
+ var expected = new[] { 0 };
95
+ Assert.Equal(expected, AllYourBase.Rebase(inputBase, inputDigits, outputBase));
95
96
  }
96
97
 
97
98
  [Fact(Skip = "Remove to run test")]
@@ -100,7 +101,8 @@ public class AllYourBaseTest
100
101
  var inputBase = 10;
101
102
  var inputDigits = new[] { 0 };
102
103
  var outputBase = 2;
103
- Assert.Throws<ArgumentException>(() => AllYourBase.Rebase(inputBase, inputDigits, outputBase));
104
+ var expected = new[] { 0 };
105
+ Assert.Equal(expected, AllYourBase.Rebase(inputBase, inputDigits, outputBase));
104
106
  }
105
107
 
106
108
  [Fact(Skip = "Remove to run test")]
@@ -109,7 +111,8 @@ public class AllYourBaseTest
109
111
  var inputBase = 10;
110
112
  var inputDigits = new[] { 0, 0, 0 };
111
113
  var outputBase = 2;
112
- Assert.Throws<ArgumentException>(() => AllYourBase.Rebase(inputBase, inputDigits, outputBase));
114
+ var expected = new[] { 0 };
115
+ Assert.Equal(expected, AllYourBase.Rebase(inputBase, inputDigits, outputBase));
113
116
  }
114
117
 
115
118
  [Fact(Skip = "Remove to run test")]
@@ -118,7 +121,8 @@ public class AllYourBaseTest
118
121
  var inputBase = 7;
119
122
  var inputDigits = new[] { 0, 6, 0 };
120
123
  var outputBase = 10;
121
- Assert.Throws<ArgumentException>(() => AllYourBase.Rebase(inputBase, inputDigits, outputBase));
124
+ var expected = new[] { 4, 2 };
125
+ Assert.Equal(expected, AllYourBase.Rebase(inputBase, inputDigits, outputBase));
122
126
  }
123
127
 
124
128
  [Fact(Skip = "Remove to run test")]
@@ -8,16 +8,20 @@ public static class AllYourBase
8
8
  {
9
9
  if (inputBase < 2) throw new ArgumentException("Invalid input base.");
10
10
  if (outputBase < 2) throw new ArgumentException("Invalid output base.");
11
- if (inputDigits.Length == 0) throw new ArgumentException("Empty input digits.");
12
11
 
13
- return ToDigits(outputBase, FromDigits(inputBase, inputDigits));
12
+ var inputDigitsWithoutLeadingZeros = inputDigits.SkipWhile(digit => digit == 0).ToArray();
13
+
14
+ if (inputDigitsWithoutLeadingZeros.Length == 0)
15
+ return new[] { 0 };
16
+
17
+ return ToDigits(outputBase, FromDigits(inputBase, inputDigitsWithoutLeadingZeros));
14
18
  }
15
19
 
16
20
  private static int FromDigits(int fromBase, int[] fromDigits)
17
21
  {
18
22
  return fromDigits.Aggregate(0, (acc, x) =>
19
23
  {
20
- if (x < 0 || x >= fromBase || (x == 0 & acc == 0)) throw new ArgumentException("Invalid input digit");
24
+ if (x < 0 || x >= fromBase) throw new ArgumentException("Invalid input digit");
21
25
 
22
26
  return acc*fromBase + x;
23
27
  });
@@ -1,4 +1,4 @@
1
- // This file was auto-generated based on version 1.0.0 of the canonical data.
1
+ // This file was auto-generated based on version 2.0.0 of the canonical data.
2
2
 
3
3
  using Xunit;
4
4
  using System;
@@ -1,4 +1,5 @@
1
1
  using System;
2
+ using System.Collections.Generic;
2
3
  using Generators.Input;
3
4
 
4
5
  namespace Generators.Exercises
@@ -9,7 +10,7 @@ namespace Generators.Exercises
9
10
  {
10
11
  foreach (var canonicalDataCase in canonicalData.Cases)
11
12
  {
12
- canonicalDataCase.ExceptionThrown = canonicalDataCase.Expected is null ? typeof(ArgumentException) : null;
13
+ canonicalDataCase.ExceptionThrown = canonicalDataCase.Expected is Dictionary<string, object> ? typeof(ArgumentException) : null;
13
14
  canonicalDataCase.UseVariablesForInput = true;
14
15
  canonicalDataCase.UseVariableForExpected = true;
15
16
  }
@@ -1,4 +1,5 @@
1
1
  using System;
2
+ using System.Collections.Generic;
2
3
  using Generators.Input;
3
4
 
4
5
  namespace Generators.Exercises
@@ -8,7 +9,7 @@ namespace Generators.Exercises
8
9
  protected override void UpdateCanonicalData(CanonicalData canonicalData)
9
10
  {
10
11
  foreach (var canonicalDataCase in canonicalData.Cases)
11
- canonicalDataCase.ExceptionThrown = canonicalDataCase.Expected is bool ? typeof(ArgumentOutOfRangeException) : null;
12
+ canonicalDataCase.ExceptionThrown = canonicalDataCase.Expected is Dictionary<string, object> ? typeof(ArgumentOutOfRangeException) : null;
12
13
  }
13
14
  }
14
15
  }
@@ -642,6 +642,17 @@
642
642
  "unlocked_by": "robot-simulator",
643
643
  "uuid": "18652e46-6dd2-4030-84af-be0965c92991"
644
644
  },
645
+ {
646
+ "core": false,
647
+ "difficulty": 4,
648
+ "slug": "isbn-verifier",
649
+ "topics": [
650
+ "loops",
651
+ "strings"
652
+ ],
653
+ "unlocked_by": "bob",
654
+ "uuid": "c339c32c-3310-4b8c-b4e6-0f9f651064b7"
655
+ },
645
656
  {
646
657
  "core": false,
647
658
  "difficulty": 5,
@@ -218,6 +218,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ReverseString", "reverse-st
218
218
  EndProject
219
219
  Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "RotationalCipher", "rotational-cipher\RotationalCipher.fsproj", "{91D0A5E0-E39C-472B-87FB-6DD17336AA22}"
220
220
  EndProject
221
+ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "IsbnVerifier", "isbn-verifier\IsbnVerifier.fsproj", "{531D1AB8-1297-4DE2-9B9B-3742A648EDB7}"
222
+ EndProject
221
223
  Global
222
224
  GlobalSection(SolutionConfigurationPlatforms) = preSolution
223
225
  Debug|Any CPU = Debug|Any CPU
@@ -656,6 +658,10 @@ Global
656
658
  {91D0A5E0-E39C-472B-87FB-6DD17336AA22}.Debug|Any CPU.Build.0 = Debug|Any CPU
657
659
  {91D0A5E0-E39C-472B-87FB-6DD17336AA22}.Release|Any CPU.ActiveCfg = Release|Any CPU
658
660
  {91D0A5E0-E39C-472B-87FB-6DD17336AA22}.Release|Any CPU.Build.0 = Release|Any CPU
661
+ {531D1AB8-1297-4DE2-9B9B-3742A648EDB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
662
+ {531D1AB8-1297-4DE2-9B9B-3742A648EDB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
663
+ {531D1AB8-1297-4DE2-9B9B-3742A648EDB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
664
+ {531D1AB8-1297-4DE2-9B9B-3742A648EDB7}.Release|Any CPU.Build.0 = Release|Any CPU
659
665
  EndGlobalSection
660
666
  GlobalSection(SolutionProperties) = preSolution
661
667
  HideSolutionNode = FALSE
@@ -0,0 +1,19 @@
1
+ module IsbnVerifier
2
+
3
+ open System.Text.RegularExpressions
4
+
5
+ let private digitToInt digit = if digit = 'X' then 10 else int digit - int '0'
6
+
7
+ let private checkSum isbn =
8
+ isbn
9
+ |> Seq.mapi (fun i digit -> (10 - i) * digitToInt digit)
10
+ |> Seq.sum
11
+
12
+ let private cleanup (isbn: string) = isbn.Replace("-", "")
13
+
14
+ let isValid (isbn: string) =
15
+ let cleanedUpIsbn = cleanup isbn
16
+
17
+ match Regex.IsMatch(cleanedUpIsbn, "^[0-9]{9}[0-9X]$") with
18
+ | false -> false
19
+ | true -> checkSum cleanedUpIsbn % 11 = 0
@@ -0,0 +1,3 @@
1
+ module IsbnVerifier
2
+
3
+ let isValid isbn = failwith "You need to implement this function."
@@ -0,0 +1,23 @@
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <TargetFramework>netcoreapp2.0</TargetFramework>
5
+
6
+ <IsPackable>false</IsPackable>
7
+ </PropertyGroup>
8
+
9
+ <ItemGroup>
10
+ <Compile Include="IsbnVerifier.fs" />
11
+ <Compile Include="IsbnVerifierTest.fs" />
12
+ <Compile Include="Program.fs" />
13
+ </ItemGroup>
14
+
15
+ <ItemGroup>
16
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
17
+ <PackageReference Include="xunit" Version="2.3.1" />
18
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
19
+ <PackageReference Include="FsUnit.xUnit" Version="3.0.0" />
20
+ <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
21
+ </ItemGroup>
22
+
23
+ </Project>
@@ -0,0 +1,61 @@
1
+ // This file was auto-generated based on version 2.0.0 of the canonical data.
2
+
3
+ module IsbnVerifierTest
4
+
5
+ open FsUnit.Xunit
6
+ open Xunit
7
+
8
+ open IsbnVerifier
9
+
10
+ [<Fact>]
11
+ let ``Valid isbn number`` () =
12
+ isValid "3-598-21508-8" |> should equal true
13
+
14
+ [<Fact(Skip = "Remove to run test")>]
15
+ let ``Invalid isbn check digit`` () =
16
+ isValid "3-598-21508-9" |> should equal false
17
+
18
+ [<Fact(Skip = "Remove to run test")>]
19
+ let ``Valid isbn number with a check digit of 10`` () =
20
+ isValid "3-598-21507-X" |> should equal true
21
+
22
+ [<Fact(Skip = "Remove to run test")>]
23
+ let ``Check digit is a character other than X`` () =
24
+ isValid "3-598-21507-A" |> should equal false
25
+
26
+ [<Fact(Skip = "Remove to run test")>]
27
+ let ``Invalid character in isbn`` () =
28
+ isValid "3-598-2K507-0" |> should equal false
29
+
30
+ [<Fact(Skip = "Remove to run test")>]
31
+ let ``X is only valid as a check digit`` () =
32
+ isValid "3-598-2X507-9" |> should equal false
33
+
34
+ [<Fact(Skip = "Remove to run test")>]
35
+ let ``Valid isbn without separating dashes`` () =
36
+ isValid "3598215088" |> should equal true
37
+
38
+ [<Fact(Skip = "Remove to run test")>]
39
+ let ``Isbn without separating dashes and X as check digit`` () =
40
+ isValid "359821507X" |> should equal true
41
+
42
+ [<Fact(Skip = "Remove to run test")>]
43
+ let ``Isbn without check digit and dashes`` () =
44
+ isValid "359821507" |> should equal false
45
+
46
+ [<Fact(Skip = "Remove to run test")>]
47
+ let ``Too long isbn and no dashes`` () =
48
+ isValid "3598215078X" |> should equal false
49
+
50
+ [<Fact(Skip = "Remove to run test")>]
51
+ let ``Isbn without check digit`` () =
52
+ isValid "3-598-21507" |> should equal false
53
+
54
+ [<Fact(Skip = "Remove to run test")>]
55
+ let ``Too long isbn`` () =
56
+ isValid "3-598-21507-XX" |> should equal false
57
+
58
+ [<Fact(Skip = "Remove to run test")>]
59
+ let ``Check digit of X should not be used for 0`` () =
60
+ isValid "3-598-21515-X" |> should equal false
61
+
@@ -0,0 +1 @@
1
+ module Program = let [<EntryPoint>] main _ = 0
@@ -0,0 +1,32 @@
1
+ Check if a given ISBN-10 is valid.
2
+
3
+ ## Functionality
4
+
5
+ Given an unknown string the program should check if the provided string is a valid ISBN-10.
6
+ Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN.
7
+
8
+ The program should allow for ISBN-10 without the separating dashes to be verified as well.
9
+
10
+ ## ISBN
11
+
12
+ Let's take a random ISBN-10 number, say `3-598-21508-8` for this.
13
+ The first digit block indicates the group where the ISBN belongs. Groups can consist of shared languages, geographic regions or countries. The leading '3' signals this ISBN is from a german speaking country.
14
+ The following number block is to identify the publisher. Since this is a three digit publisher number there is a 5 digit title number for this book.
15
+ The last digit in the ISBN is the check digit which is used to detect read errors.
16
+
17
+ The first 9 digits in the ISBN have to be between 0 and 9.
18
+ The check digit can additionally be an 'X' to allow 10 to be a valid check digit as well.
19
+
20
+ A valid ISBN-10 is calculated with this formula `(x1 * 10 + x2 * 9 + x3 * 8 + x4 * 7 + x5 * 6 + x6 * 5 + x7 * 4 + x8 * 3 + x9 * 2 + x10 * 1) mod 11 == 0`
21
+ So for our example ISBN this means:
22
+ (3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 = 0
23
+
24
+ Which proves that the ISBN is valid.
25
+
26
+ ## Caveats
27
+
28
+ Converting from string to number can be tricky in certain languages.
29
+ It's getting even trickier since the check-digit of an ISBN-10 can be 'X'.
30
+
31
+ ## Submitting Incomplete Solutions
32
+ It's possible to submit an incomplete solution so you can see how others have completed the exercise.
@@ -318,6 +318,9 @@ type House() =
318
318
  |> Seq.map formatValue
319
319
  |> formatMultiLineList
320
320
 
321
+ type IsbnVerifier() =
322
+ inherit Exercise()
323
+
321
324
  type Isogram() =
322
325
  inherit Exercise()
323
326
 
@@ -0,0 +1,46 @@
1
+ package main
2
+
3
+ import (
4
+ "log"
5
+ "text/template"
6
+
7
+ "../../../gen"
8
+ )
9
+
10
+ func main() {
11
+ t, err := template.New("").Parse(tmpl)
12
+ if err != nil {
13
+ log.Fatal(err)
14
+ }
15
+ var j js
16
+ if err := gen.Gen("sieve", &j, t); err != nil {
17
+ log.Fatal(err)
18
+ }
19
+ }
20
+
21
+ // The JSON structure we expect to be able to unmarshal into
22
+ type js struct {
23
+ Cases []struct {
24
+ Description string
25
+ Limit int
26
+ Expected []int
27
+ }
28
+ }
29
+
30
+ // template applied to above data structure generates the Go test cases
31
+ var tmpl = `package sieve
32
+
33
+ {{.Header}}
34
+
35
+ var testCases = []struct {
36
+ description string
37
+ limit int
38
+ expected []int
39
+ }{
40
+ {{range .J.Cases}}{
41
+ "{{.Description}}",
42
+ {{.Limit}},
43
+ {{printf "%#v" .Expected}},
44
+ },
45
+ {{end}}}
46
+ `
@@ -0,0 +1,37 @@
1
+ package sieve
2
+
3
+ // Source: exercism/problem-specifications
4
+ // Commit: f2b2693 sieve: Fix canonical-data.json formatting
5
+ // Problem Specifications Version: 1.0.0
6
+
7
+ var testCases = []struct {
8
+ description string
9
+ limit int
10
+ expected []int
11
+ }{
12
+ {
13
+ "no primes under two",
14
+ 1,
15
+ []int{},
16
+ },
17
+ {
18
+ "find first prime",
19
+ 2,
20
+ []int{2},
21
+ },
22
+ {
23
+ "find primes up to 10",
24
+ 10,
25
+ []int{2, 3, 5, 7},
26
+ },
27
+ {
28
+ "limit is prime",
29
+ 13,
30
+ []int{2, 3, 5, 7, 11, 13},
31
+ },
32
+ {
33
+ "find primes up to 1000",
34
+ 1000,
35
+ []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997},
36
+ },
37
+ }
@@ -1,15 +1,15 @@
1
1
  package sieve
2
2
 
3
3
  func Sieve(limit int) (primes []int) {
4
- c := make([]bool, limit)
5
- for p := 2; p < limit; {
6
- for i := p + p; i < limit; i += p {
4
+ c := make([]bool, limit+1)
5
+ for p := 2; p <= limit; {
6
+ for i := p + p; i <= limit; i += p {
7
7
  c[i] = true
8
8
  }
9
- for p++; p < limit && c[p]; p++ {
9
+ for p++; p <= limit && c[p]; p++ {
10
10
  }
11
11
  }
12
- for i := 2; i < limit; i++ {
12
+ for i := 2; i <= limit; i++ {
13
13
  if !c[i] {
14
14
  primes = append(primes, i)
15
15
  }
@@ -5,32 +5,23 @@ import (
5
5
  "testing"
6
6
  )
7
7
 
8
- var p10 = []int{2, 3, 5, 7}
9
- var p1000 = []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53,
10
- 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
11
- 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223,
12
- 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
13
- 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397,
14
- 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487,
15
- 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593,
16
- 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
17
- 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787,
18
- 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883,
19
- 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997}
20
-
21
8
  func TestSieve(t *testing.T) {
22
- p := Sieve(10)
23
- if !reflect.DeepEqual(p, p10) {
24
- t.Fatalf("Sieve(10) = %v, want %v", p, p10)
25
- }
26
- p = Sieve(1000)
27
- if !reflect.DeepEqual(p, p1000) {
28
- t.Fatalf("Sieve(1000) = %v, want %v", p, p1000)
9
+ for _, tc := range testCases {
10
+ p := Sieve(tc.limit)
11
+ if len(p) != 0 || len(tc.expected) != 0 {
12
+ if !reflect.DeepEqual(p, tc.expected) {
13
+ t.Fatalf("FAIL: %s\nSieve(%d)\nExpected %v\nActual %v",
14
+ tc.description, tc.limit, tc.expected, p)
15
+ }
16
+ }
17
+ t.Logf("PASS: %s", tc.description)
29
18
  }
30
19
  }
31
20
 
32
21
  func BenchmarkSieve(b *testing.B) {
33
22
  for i := 0; i < b.N; i++ {
34
- Sieve(1000)
23
+ for _, tc := range testCases {
24
+ Sieve(tc.limit)
25
+ }
35
26
  }
36
27
  }
@@ -2,6 +2,7 @@ import org.junit.Ignore;
2
2
  import org.junit.Test;
3
3
 
4
4
  import java.util.Arrays;
5
+ import java.util.Collections;
5
6
  import java.util.List;
6
7
 
7
8
  import static org.hamcrest.CoreMatchers.*;
@@ -53,7 +54,7 @@ public class AnagramTest {
53
54
  @Test
54
55
  public void testDoesNotConfuseDifferentDuplicates() {
55
56
  Anagram detector = new Anagram("galea");
56
- assertTrue(detector.match(Arrays.asList("eagle")).isEmpty());
57
+ assertTrue(detector.match(Collections.singletonList("eagle")).isEmpty());
57
58
  }
58
59
 
59
60
  @Ignore("Remove to run test")
@@ -69,21 +70,21 @@ public class AnagramTest {
69
70
  @Test
70
71
  public void testIdenticalWordRepeatedIsNotAnagram() {
71
72
  Anagram detector = new Anagram("go");
72
- assertTrue(detector.match(Arrays.asList("go Go GO")).isEmpty());
73
+ assertTrue(detector.match(Collections.singletonList("go Go GO")).isEmpty());
73
74
  }
74
75
 
75
76
  @Ignore("Remove to run test")
76
77
  @Test
77
78
  public void testCapitalWordIsNotOwnAnagram() {
78
79
  Anagram detector = new Anagram("BANANA");
79
- assertTrue(detector.match(Arrays.asList("Banana")).isEmpty());
80
+ assertTrue(detector.match(Collections.singletonList("Banana")).isEmpty());
80
81
  }
81
82
 
82
83
  @Ignore("Remove to run test")
83
84
  @Test
84
85
  public void testEliminateAnagramsWithSameChecksum() {
85
86
  Anagram detector = new Anagram("mass");
86
- assertTrue(detector.match(Arrays.asList("last")).isEmpty());
87
+ assertTrue(detector.match(Collections.singletonList("last")).isEmpty());
87
88
  }
88
89
 
89
90
  @Ignore("Remove to run test")
@@ -121,13 +122,13 @@ public class AnagramTest {
121
122
  @Test
122
123
  public void testWordIsNotItsOwnAnagram() {
123
124
  Anagram detector = new Anagram("banana");
124
- assertTrue(detector.match(Arrays.asList("Banana")).isEmpty());
125
+ assertTrue(detector.match(Collections.singletonList("Banana")).isEmpty());
125
126
  }
126
127
 
127
128
  @Ignore("Remove to run test")
128
129
  @Test
129
130
  public void testAnagramMustUseAllLettersExactlyOnce() {
130
131
  Anagram detector = new Anagram("tapper");
131
- assertTrue(detector.match(Arrays.asList("patter")).isEmpty());
132
+ assertTrue(detector.match(Collections.singletonList("patter")).isEmpty());
132
133
  }
133
134
  }
@@ -413,6 +413,19 @@
413
413
  "unlocked_by": null,
414
414
  "uuid": "f00fe4b4-7caf-4fe0-b8e6-c1684f71c5f6"
415
415
  },
416
+ {
417
+ "core": false,
418
+ "difficulty": 4,
419
+ "slug": "say",
420
+ "topics": [
421
+ "loops",
422
+ "parsing",
423
+ "transforming",
424
+ "text_formatting"
425
+ ],
426
+ "unlocked_by": null,
427
+ "uuid": "9292d37e-2b5f-45a2-b995-ebcce9d90376"
428
+ },
416
429
  {
417
430
  "core": false,
418
431
  "difficulty": 5,
@@ -0,0 +1,7 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ @interface Say : NSObject
4
+
5
+ + (NSString *)say:(long)number;
6
+
7
+ @end
@@ -0,0 +1,55 @@
1
+ #import "SayExample.h"
2
+
3
+ @implementation Say
4
+
5
+ + (NSString *)say:(long)number {
6
+ NSArray<NSString *> *smallNumbers = @[
7
+ @"zero", @"one", @"two", @"three", @"four", @"five",
8
+ @"six", @"seven", @"eight", @"nine", @"ten",
9
+ @"eleven", @"twelve", @"thirteen", @"fourteen", @"fifteen",
10
+ @"sixteen", @"seventeen", @"eighteen", @"nineteen"
11
+ ];
12
+
13
+ NSArray<NSString *> *decades = @[
14
+ @"twenty", @"thirty", @"forty", @"fifty",
15
+ @"sixty", @"seventy", @"eighty", @"ninety"
16
+ ];
17
+
18
+ NSArray<NSString *> *largeGroupNames = @[@"billion", @"million", @"thousand", @"hundred"];
19
+
20
+ NSArray<NSNumber *> *largeGroupAmounts = @[@1000000000, @1000000, @1000, @100];
21
+
22
+ if (number < 0 || number >= 1000000000000) {
23
+ return NULL;
24
+ }
25
+
26
+ if (number < 20) {
27
+ return smallNumbers[number];
28
+ }
29
+
30
+ for (int i = 0; i < largeGroupAmounts.count; i++) {
31
+ int amount = largeGroupAmounts[i].intValue;
32
+
33
+ if (number >= amount) {
34
+ NSString *groupName = largeGroupNames[i];
35
+ NSString *result = [[Say say:(number / amount)] stringByAppendingString:[NSString stringWithFormat:@" %@", groupName]];
36
+ long remainder = number % amount;
37
+
38
+ if (remainder == 0) {
39
+ return result;
40
+ }
41
+ return [result stringByAppendingString:[NSString stringWithFormat:@" %@", [Say say:remainder]]];
42
+ }
43
+ }
44
+
45
+ long decade = number / 10;
46
+ int remainder = number % 10;
47
+ NSString *decadeName = decades[decade - 2];
48
+
49
+ if (remainder == 0) {
50
+ return decadeName;
51
+ }
52
+ return [decadeName stringByAppendingString:[NSString stringWithFormat:@"-%@", [Say say:remainder]]];
53
+ }
54
+
55
+ @end
@@ -0,0 +1,75 @@
1
+ #import <XCTest/XCTest.h>
2
+
3
+ #if __has_include("SayExample.h")
4
+ # import "SayExample.h"
5
+ #else
6
+ # import "Say.h"
7
+ #endif
8
+
9
+ @interface SayTest : XCTestCase
10
+
11
+ @end
12
+
13
+ @implementation SayTest
14
+
15
+ - (void)testZero {
16
+ XCTAssertEqualObjects(@"zero", [Say say:0]);
17
+ }
18
+
19
+ - (void)testOne {
20
+ XCTAssertEqualObjects(@"one", [Say say:1]);
21
+ }
22
+
23
+ - (void)testFourteen {
24
+ XCTAssertEqualObjects(@"fourteen", [Say say:14]);
25
+ }
26
+
27
+ - (void)testTwenty {
28
+ XCTAssertEqualObjects(@"twenty", [Say say:20]);
29
+ }
30
+
31
+ - (void)testTwentyTwo {
32
+ XCTAssertEqualObjects(@"twenty-two", [Say say:22]);
33
+ }
34
+
35
+ - (void)testOneHundred {
36
+ XCTAssertEqualObjects(@"one hundred", [Say say:100]);
37
+ }
38
+
39
+ - (void)testOneHundredTwentyThree {
40
+ XCTAssertEqualObjects(@"one hundred twenty-three", [Say say:123]);
41
+ }
42
+
43
+ - (void)testOneThousand {
44
+ XCTAssertEqualObjects(@"one thousand", [Say say:1000]);
45
+ }
46
+
47
+ - (void)testOneThousandTwoHundredThirtyFour {
48
+ XCTAssertEqualObjects(@"one thousand two hundred thirty-four", [Say say:1234]);
49
+ }
50
+
51
+ - (void)testOneMillion {
52
+ XCTAssertEqualObjects(@"one million", [Say say:1000000]);
53
+ }
54
+
55
+ - (void)testOneMillionTwoThousandThreeHundredFortyFive {
56
+ XCTAssertEqualObjects(@"one million two thousand three hundred forty-five", [Say say:1002345]);
57
+ }
58
+
59
+ - (void)testOneBillion {
60
+ XCTAssertEqualObjects(@"one billion", [Say say:1000000000]);
61
+ }
62
+
63
+ - (void)testABigNumber {
64
+ XCTAssertEqualObjects(@"nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three", [Say say:987654321123]);
65
+ }
66
+
67
+ - (void)testNumbersBelowZeroAreOutOfRange {
68
+ XCTAssertNil([Say say:-1]);
69
+ }
70
+
71
+ - (void)testNumbersAbove999999999999AreOutOfRange {
72
+ XCTAssertNil([Say say:1000000000000]);
73
+ }
74
+
75
+ @end
@@ -79,6 +79,8 @@
79
79
  E951B6B91D429550009EB5B6 /* AllergiesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E951B6B81D429550009EB5B6 /* AllergiesTest.m */; };
80
80
  E95C52551E81C82A0095D321 /* BinarySearchExample.m in Sources */ = {isa = PBXBuildFile; fileRef = E95C52531E81C82A0095D321 /* BinarySearchExample.m */; };
81
81
  E95C52561E81C82A0095D321 /* BinarySearchTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E95C52541E81C82A0095D321 /* BinarySearchTest.m */; };
82
+ E964F68F1FBF9CA6000114D9 /* SayExample.m in Sources */ = {isa = PBXBuildFile; fileRef = E964F68E1FBF9CA6000114D9 /* SayExample.m */; };
83
+ E964F6911FBF9CC6000114D9 /* SayTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E964F6901FBF9CC6000114D9 /* SayTest.m */; };
82
84
  E96993981DF60E1E009EA223 /* TransposeExample.m in Sources */ = {isa = PBXBuildFile; fileRef = E96993971DF60E1E009EA223 /* TransposeExample.m */; };
83
85
  E969939A1DF60E5F009EA223 /* TransposeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E96993991DF60E5F009EA223 /* TransposeTest.m */; };
84
86
  E973200C1E9DA0A900ABEE5C /* SieveExample.m in Sources */ = {isa = PBXBuildFile; fileRef = E973200B1E9DA0A900ABEE5C /* SieveExample.m */; };
@@ -197,6 +199,9 @@
197
199
  E95C52521E81C82A0095D321 /* BinarySearchExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BinarySearchExample.h; path = "../../exercises/binary-search/BinarySearchExample.h"; sourceTree = "<group>"; };
198
200
  E95C52531E81C82A0095D321 /* BinarySearchExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BinarySearchExample.m; path = "../../exercises/binary-search/BinarySearchExample.m"; sourceTree = "<group>"; };
199
201
  E95C52541E81C82A0095D321 /* BinarySearchTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BinarySearchTest.m; path = "../../exercises/binary-search/BinarySearchTest.m"; sourceTree = "<group>"; };
202
+ E964F68D1FBF9CA6000114D9 /* SayExample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SayExample.h; path = ../../exercises/say/SayExample.h; sourceTree = "<group>"; };
203
+ E964F68E1FBF9CA6000114D9 /* SayExample.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SayExample.m; path = ../../exercises/say/SayExample.m; sourceTree = "<group>"; };
204
+ E964F6901FBF9CC6000114D9 /* SayTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SayTest.m; path = ../../exercises/say/SayTest.m; sourceTree = "<group>"; };
200
205
  E96993961DF60E1E009EA223 /* TransposeExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TransposeExample.h; path = ../../exercises/transpose/TransposeExample.h; sourceTree = "<group>"; };
201
206
  E96993971DF60E1E009EA223 /* TransposeExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TransposeExample.m; path = ../../exercises/transpose/TransposeExample.m; sourceTree = "<group>"; };
202
207
  E96993991DF60E5F009EA223 /* TransposeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TransposeTest.m; path = ../../exercises/transpose/TransposeTest.m; sourceTree = "<group>"; };
@@ -285,6 +290,7 @@
285
290
  E9E8B6F51D519E2E0012F12C /* RobotName */,
286
291
  E9FDCA161D540793004EE8DB /* RomanNumerals */,
287
292
  E9C1C02C1D9EC0E50015E86E /* RunLengthEncoding */,
293
+ E964F68C1FBF9B6B000114D9 /* Say */,
288
294
  E907FE8F1D87545300B93DA9 /* ScrabbleScore */,
289
295
  E9C1C0201D9D98B80015E86E /* SecretHandshake */,
290
296
  E97320091E9DA06500ABEE5C /* Sieve */,
@@ -456,6 +462,16 @@
456
462
  name = BinarySearch;
457
463
  sourceTree = "<group>";
458
464
  };
465
+ E964F68C1FBF9B6B000114D9 /* Say */ = {
466
+ isa = PBXGroup;
467
+ children = (
468
+ E964F68D1FBF9CA6000114D9 /* SayExample.h */,
469
+ E964F68E1FBF9CA6000114D9 /* SayExample.m */,
470
+ E964F6901FBF9CC6000114D9 /* SayTest.m */,
471
+ );
472
+ name = Say;
473
+ sourceTree = "<group>";
474
+ };
459
475
  E96993951DF60DF1009EA223 /* Transpose */ = {
460
476
  isa = PBXGroup;
461
477
  children = (
@@ -873,6 +889,7 @@
873
889
  1EFACABA1CCCAF3D006F2E69 /* SpaceAgeTest.m in Sources */,
874
890
  1EFACAB91CCCAF3D006F2E69 /* SpaceAgeExample.m in Sources */,
875
891
  E9F390091DFCA350005C5F46 /* IsogramTest.m in Sources */,
892
+ E964F6911FBF9CC6000114D9 /* SayTest.m in Sources */,
876
893
  E951B6B71D4294E6009EB5B6 /* AllergiesExample.m in Sources */,
877
894
  E9FDCA1B1D540801004EE8DB /* RomanNumeralsTest.m in Sources */,
878
895
  E9C1C0231D9D993E0015E86E /* SecretHandshakeExample.m in Sources */,
@@ -895,6 +912,7 @@
895
912
  E9386EEE1E0B692D0009A414 /* AtbashCipherExample.m in Sources */,
896
913
  1EFACAA61CCCAF3D006F2E69 /* BobTest.m in Sources */,
897
914
  E9B345FA1DB93839006EFBE2 /* PangramTest.m in Sources */,
915
+ E964F68F1FBF9CA6000114D9 /* SayExample.m in Sources */,
898
916
  E9895B701E8DA914006AD25D /* CryptoSquareTest.m in Sources */,
899
917
  E9B9F2D41E9EB39C00214076 /* LuhnExample.m in Sources */,
900
918
  E9A7B2F91DA5AC55009056B6 /* LargestSeriesProductTest.m in Sources */,
@@ -5,6 +5,7 @@ for the school.
5
5
 
6
6
  In the end, you should be able to:
7
7
 
8
+ - Start with an empty school.
8
9
  - Add a student's name to the roster for a grade
9
10
  - "Add Jim to grade 2."
10
11
  - "OK."
@@ -22,20 +23,6 @@ Note that all our students only have one name. (It's a small town, what
22
23
  do you want?)
23
24
 
24
25
 
25
- ## For bonus points
26
-
27
- Did you get the tests passing and the code clean? If you want to, these
28
- are some additional things you could try:
29
-
30
- - If you're working in a language with mutable data structures and your
31
- implementation allows outside code to mutate the school's internal DB
32
- directly, see if you can prevent this. Feel free to introduce additional
33
- tests.
34
-
35
- Then please share your thoughts in a comment on the submission. Did this
36
- experiment make the code better? Worse? Did you learn anything from it?
37
-
38
-
39
26
  ## Getting Started
40
27
  For installation and learning resources, refer to the
41
28
  [exercism help page](http://exercism.io/languages/ocaml).
@@ -2,7 +2,7 @@ open Core
2
2
 
3
3
  type school = string list Int.Map.t
4
4
 
5
- let create _ = Int.Map.empty
5
+ let empty_school = Int.Map.empty
6
6
 
7
7
  let add s g school = Map.add_multi ~key:g ~data:s school
8
8
 
@@ -3,8 +3,7 @@ open Core
3
3
 
4
4
  type school
5
5
 
6
- (** Create a new empty school *)
7
- val create : unit -> school
6
+ val empty_school : school
8
7
 
9
8
  (** Add a student to a school *)
10
9
  val add : string -> int -> school -> school
@@ -3,14 +3,12 @@ open OUnit2
3
3
 
4
4
  module IMap = Int.Map
5
5
 
6
- (* Assert list equals *)
7
- let ale exp got =
6
+ let assert_list_equals exp got =
8
7
  let printer l = List.sexp_of_t String.sexp_of_t l
9
8
  |> Sexp.to_string_hum ~indent:1 in
10
9
  assert_equal exp got ~printer
11
10
 
12
- (* Assert map equals *)
13
- let ame exp got =
11
+ let assert_map_equals exp got =
14
12
  let printer m =
15
13
  Map.to_alist m
16
14
  |> List.sort ~cmp:compare
@@ -18,52 +16,44 @@ let ame exp got =
18
16
  |> Sexp.to_string_hum ~indent:1 in
19
17
  assert_equal exp got ~cmp:(Map.equal (=)) ~printer
20
18
 
21
- (* The tests never reuse a school value, so if you like you can use destructive
22
- * modification (i.e. mutable data structures). For the same reason
23
- * Grade_school.create takes a unit argument.
24
- *
25
- * For those who wonder why the test doesn't use objects, it's to not force
26
- * the use of objects. You can still use them internally though.
27
- *)
28
-
29
19
  let tests =
30
20
  ["add student">:: (fun _ ->
31
- let got = Grade_school.create ()
21
+ let got = Grade_school.empty_school
32
22
  |> Grade_school.add "Aimee" 2 in
33
- ame (IMap.of_alist_exn [(2, ["Aimee"])])
23
+ assert_map_equals (IMap.of_alist_exn [(2, ["Aimee"])])
34
24
  (Grade_school.to_map got));
35
- "add more students in same class">:: (fun _ ->
36
- let got = Grade_school.create ()
37
- |> Grade_school.add "James" 2
25
+ "add more students in sassert_map_equals class">:: (fun _ ->
26
+ let got = Grade_school.empty_school
27
+ |> Grade_school.add "Jassert_map_equalss" 2
38
28
  |> Grade_school.add "Blair" 2
39
29
  |> Grade_school.add "Paul" 2 in
40
- ame (IMap.of_alist_exn [(2, ["Blair"; "James"; "Paul"])])
30
+ assert_map_equals (IMap.of_alist_exn [(2, ["Blair"; "Jassert_map_equalss"; "Paul"])])
41
31
  (Grade_school.to_map got |> Map.map ~f:(List.sort ~cmp:compare)));
42
32
  "add students to different grades">:: (fun _ ->
43
- let got = Grade_school.create ()
33
+ let got = Grade_school.empty_school
44
34
  |> Grade_school.add "Chelsea" 3
45
35
  |> Grade_school.add "Logan" 7 in
46
- ame (IMap.of_alist_exn [(3, ["Chelsea"]); (7, ["Logan"])])
36
+ assert_map_equals (IMap.of_alist_exn [(3, ["Chelsea"]); (7, ["Logan"])])
47
37
  (Grade_school.to_map got |> Map.map ~f:(List.sort ~cmp:compare)));
48
38
  "get students in a grade">:: (fun _ ->
49
- let got = Grade_school.create ()
39
+ let got = Grade_school.empty_school
50
40
  |> Grade_school.add "Franklin" 5
51
41
  |> Grade_school.add "Bradley" 5
52
42
  |> Grade_school.add "Jeff" 1
53
43
  |> Grade_school.grade 5 in
54
- ale ["Bradley"; "Franklin"]
44
+ assert_list_equals ["Bradley"; "Franklin"]
55
45
  (List.sort ~cmp:compare got));
56
46
  "get students in a non existant grade">:: (fun _ ->
57
- ale [] (List.sort ~cmp:compare (Grade_school.create () |> Grade_school.grade 2)));
47
+ assert_list_equals [] (List.sort ~cmp:compare (Grade_school.empty_school |> Grade_school.grade 2)));
58
48
  "sort school">:: (fun _ ->
59
- let got = Grade_school.create ()
49
+ let got = Grade_school.empty_school
60
50
  |> Grade_school.add "Christopher" 4
61
51
  |> Grade_school.add "Jennifer" 4
62
52
  |> Grade_school.add "Aaron" 4
63
53
  |> Grade_school.add "Kareem" 6
64
54
  |> Grade_school.add "Kyle" 3
65
55
  |> Grade_school.sort in
66
- ame (IMap.of_alist_exn [(3, ["Kyle"]);
56
+ assert_map_equals (IMap.of_alist_exn [(3, ["Kyle"]);
67
57
  (4, ["Aaron"; "Christopher"; "Jennifer"]);
68
58
  (6, ["Kareem"])])
69
59
  (Grade_school.to_map got));
@@ -572,6 +572,19 @@
572
572
  "unlocked_by": null,
573
573
  "uuid": "f6b157cb-ddb0-4c40-baa3-9fc6b58de7ff"
574
574
  },
575
+ {
576
+ "core": false,
577
+ "difficulty": 4,
578
+ "slug": "say",
579
+ "topics": [
580
+ "loops",
581
+ "parsing",
582
+ "transforming",
583
+ "text_formatting"
584
+ ],
585
+ "unlocked_by": null,
586
+ "uuid": "0f840c37-a8c0-42da-93f9-d215cb044d53"
587
+ },
575
588
  {
576
589
  "core": false,
577
590
  "difficulty": 5,
@@ -0,0 +1,5 @@
1
+ import PackageDescription
2
+
3
+ let package = Package(
4
+ name: "Say"
5
+ )
@@ -0,0 +1,77 @@
1
+ # Say
2
+
3
+ Given a number from 0 to 999,999,999,999, spell out that number in English.
4
+
5
+ ## Step 1
6
+
7
+ Handle the basic case of 0 through 99.
8
+
9
+ If the input to the program is `22`, then the output should be
10
+ `'twenty-two'`.
11
+
12
+ Your program should complain loudly if given a number outside the
13
+ blessed range.
14
+
15
+ Some good test cases for this program are:
16
+
17
+ - 0
18
+ - 14
19
+ - 50
20
+ - 98
21
+ - -1
22
+ - 100
23
+
24
+ ### Extension
25
+
26
+ If you're on a Mac, shell out to Mac OS X's `say` program to talk out
27
+ loud.
28
+
29
+ ## Step 2
30
+
31
+ Implement breaking a number up into chunks of thousands.
32
+
33
+ So `1234567890` should yield a list like 1, 234, 567, and 890, while the
34
+ far simpler `1000` should yield just 1 and 0.
35
+
36
+ The program must also report any values that are out of range.
37
+
38
+ ## Step 3
39
+
40
+ Now handle inserting the appropriate scale word between those chunks.
41
+
42
+ So `1234567890` should yield `'1 billion 234 million 567 thousand 890'`
43
+
44
+ The program must also report any values that are out of range. It's
45
+ fine to stop at "trillion".
46
+
47
+ ## Step 4
48
+
49
+ Put it all together to get nothing but plain English.
50
+
51
+ `12345` should give `twelve thousand three hundred forty-five`.
52
+
53
+ The program must also report any values that are out of range.
54
+
55
+ ### Extensions
56
+
57
+ Use _and_ (correctly) when spelling out the number in English:
58
+
59
+ - 14 becomes "fourteen".
60
+ - 100 becomes "one hundred".
61
+ - 120 becomes "one hundred and twenty".
62
+ - 1002 becomes "one thousand and two".
63
+ - 1323 becomes "one thousand three hundred and twenty-three".
64
+
65
+ ## Setup
66
+
67
+ Go through the project setup instructions for Xcode using Swift:
68
+
69
+ http://exercism.io/languages/swift
70
+
71
+ ## Source
72
+
73
+ A variation on JavaRanch CattleDrive, exercise 4a [http://www.javaranch.com/say.jsp](http://www.javaranch.com/say.jsp)
74
+
75
+ ## Submitting Incomplete Solutions
76
+
77
+ It's possible to submit an incomplete solution so you can see how others have completed the exercise.
@@ -0,0 +1 @@
1
+ //Solution goes in Sources
@@ -0,0 +1,48 @@
1
+ struct Say {
2
+
3
+ private static let smallNumbers = [
4
+ "zero", "one", "two", "three", "four", "five",
5
+ "six", "seven", "eight", "nine", "ten",
6
+ "eleven", "twelve", "thirteen", "fourteen", "fifteen",
7
+ "sixteen", "seventeen", "eighteen", "nineteen"
8
+ ]
9
+
10
+ private static let decades = [
11
+ "twenty", "thirty", "forty", "fifty",
12
+ "sixty", "seventy", "eighty", "ninety"
13
+ ]
14
+
15
+ private static let largeGroups: [(name: String, amount: Int)] = [
16
+ ("billion", 1_000_000_000), ("million", 1_000_000), ("thousand", 1_000), ("hundred", 100)
17
+ ]
18
+
19
+ static func say(_ number: Int) -> String? {
20
+ guard number >= 0 && number < 1_000_000_000_000 else {
21
+ return nil
22
+ }
23
+
24
+ if number < 20 {
25
+ return smallNumbers[number]
26
+ }
27
+
28
+ for group in largeGroups where number >= group.amount {
29
+ let result = "\(say(number / group.amount)!) " + group.name
30
+ let remainder = number % group.amount
31
+
32
+ if remainder == 0 {
33
+ return result
34
+ } else {
35
+ return result + " \(say(remainder)!)"
36
+ }
37
+ }
38
+
39
+ let decade = number / 10
40
+ let decadeName = decades[decade - 2]
41
+
42
+ if number % 10 == 0 {
43
+ return decadeName
44
+ } else {
45
+ return decadeName + "-" + say(number % 10)!
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,6 @@
1
+ import XCTest
2
+ @testable import SayTests
3
+
4
+ XCTMain([
5
+ testCase(SayTests.allTests),
6
+ ])
@@ -0,0 +1,85 @@
1
+ import XCTest
2
+ @testable import Say
3
+
4
+ class SayTests: XCTestCase {
5
+
6
+ func testZero() {
7
+ XCTAssertEqual("zero", Say.say(0))
8
+ }
9
+
10
+ func testOne() {
11
+ XCTAssertEqual("one", Say.say(1))
12
+ }
13
+
14
+ func testFourteen() {
15
+ XCTAssertEqual("fourteen", Say.say(14))
16
+ }
17
+
18
+ func testTwenty() {
19
+ XCTAssertEqual("twenty", Say.say(20))
20
+ }
21
+
22
+ func testTwentyTwo() {
23
+ XCTAssertEqual("twenty-two", Say.say(22))
24
+ }
25
+
26
+ func testOneHundred() {
27
+ XCTAssertEqual("one hundred", Say.say(100))
28
+ }
29
+
30
+ func testOneHundredTwentyThree() {
31
+ XCTAssertEqual("one hundred twenty-three", Say.say(123))
32
+ }
33
+
34
+ func testOneThousand() {
35
+ XCTAssertEqual("one thousand", Say.say(1_000))
36
+ }
37
+
38
+ func testOneThousandTwoHundredThirtyFour() {
39
+ XCTAssertEqual("one thousand two hundred thirty-four", Say.say(1_234))
40
+ }
41
+
42
+ func testOneMillion() {
43
+ XCTAssertEqual("one million", Say.say(1_000_000))
44
+ }
45
+
46
+ func testOneMillionTwoThousandThreeHundredFortyFive() {
47
+ XCTAssertEqual("one million two thousand three hundred forty-five", Say.say(1_002_345))
48
+ }
49
+
50
+ func testOneBillion() {
51
+ XCTAssertEqual("one billion", Say.say(1_000_000_000))
52
+ }
53
+
54
+ func testABigNumber() {
55
+ XCTAssertEqual("nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three", Say.say(987_654_321_123))
56
+ }
57
+
58
+ func testNumbersBelowZeroAreOutOfRange() {
59
+ XCTAssertNil(Say.say(-1))
60
+ }
61
+
62
+ func testNumbersAbove999999999999AreOutOfRange() {
63
+ XCTAssertNil(Say.say(1_000_000_000_000))
64
+ }
65
+
66
+ static var allTests: [(String, (SayTests) -> () throws -> Void)] {
67
+ return [
68
+ ("testZero", testZero),
69
+ ("testOne", testOne),
70
+ ("testFourteen", testFourteen),
71
+ ("testTwenty", testTwenty),
72
+ ("testTwentyTwo", testTwentyTwo),
73
+ ("testOneHundred", testOneHundred),
74
+ ("testOneHundredTwentyThree", testOneHundredTwentyThree),
75
+ ("testOneThousand", testOneThousand),
76
+ ("testOneThousandTwoHundredThirtyFour", testOneThousandTwoHundredThirtyFour),
77
+ ("testOneMillion", testOneMillion),
78
+ ("testOneMillionTwoThousandThreeHundredFortyFive", testOneMillionTwoThousandThreeHundredFortyFive),
79
+ ("testOneBillion", testOneBillion),
80
+ ("testABigNumber", testABigNumber),
81
+ ("testNumbersBelowZeroAreOutOfRange", testNumbersBelowZeroAreOutOfRange),
82
+ ("testNumbersAbove999999999999AreOutOfRange", testNumbersAbove999999999999AreOutOfRange),
83
+ ]
84
+ }
85
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trackler
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1.67
4
+ version: 2.2.1.68
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katrina Owen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-17 00:00:00.000000000 Z
11
+ date: 2017-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -5145,6 +5145,12 @@ files:
5145
5145
  - tracks/fsharp/exercises/house/HouseTest.fs
5146
5146
  - tracks/fsharp/exercises/house/Program.fs
5147
5147
  - tracks/fsharp/exercises/house/README.md
5148
+ - tracks/fsharp/exercises/isbn-verifier/Example.fs
5149
+ - tracks/fsharp/exercises/isbn-verifier/IsbnVerifier.fs
5150
+ - tracks/fsharp/exercises/isbn-verifier/IsbnVerifier.fsproj
5151
+ - tracks/fsharp/exercises/isbn-verifier/IsbnVerifierTest.fs
5152
+ - tracks/fsharp/exercises/isbn-verifier/Program.fs
5153
+ - tracks/fsharp/exercises/isbn-verifier/README.md
5148
5154
  - tracks/fsharp/exercises/isogram/Example.fs
5149
5155
  - tracks/fsharp/exercises/isogram/Isogram.fs
5150
5156
  - tracks/fsharp/exercises/isogram/Isogram.fsproj
@@ -5995,7 +6001,9 @@ files:
5995
6001
  - tracks/go/exercises/series/first_example.go
5996
6002
  - tracks/go/exercises/series/first_test.go
5997
6003
  - tracks/go/exercises/series/series_test.go
6004
+ - tracks/go/exercises/sieve/.meta/gen.go
5998
6005
  - tracks/go/exercises/sieve/README.md
6006
+ - tracks/go/exercises/sieve/cases_test.go
5999
6007
  - tracks/go/exercises/sieve/example.go
6000
6008
  - tracks/go/exercises/sieve/sieve_test.go
6001
6009
  - tracks/go/exercises/simple-cipher/.meta/description.md
@@ -9178,6 +9186,9 @@ files:
9178
9186
  - tracks/objective-c/exercises/run-length-encoding/RunLengthEncodingExample.h
9179
9187
  - tracks/objective-c/exercises/run-length-encoding/RunLengthEncodingExample.m
9180
9188
  - tracks/objective-c/exercises/run-length-encoding/RunLengthEncodingTest.m
9189
+ - tracks/objective-c/exercises/say/SayExample.h
9190
+ - tracks/objective-c/exercises/say/SayExample.m
9191
+ - tracks/objective-c/exercises/say/SayTest.m
9181
9192
  - tracks/objective-c/exercises/scrabble-score/README.md
9182
9193
  - tracks/objective-c/exercises/scrabble-score/ScrabbleScoreExample.h
9183
9194
  - tracks/objective-c/exercises/scrabble-score/ScrabbleScoreExample.m
@@ -13511,6 +13522,12 @@ files:
13511
13522
  - tracks/swift/exercises/saddle-points/Sources/SaddlePointsExample.swift
13512
13523
  - tracks/swift/exercises/saddle-points/Tests/LinuxMain.swift
13513
13524
  - tracks/swift/exercises/saddle-points/Tests/SaddlePointsTests/SaddlePointsTests.swift
13525
+ - tracks/swift/exercises/say/Package.swift
13526
+ - tracks/swift/exercises/say/README.md
13527
+ - tracks/swift/exercises/say/Sources/Say.swift
13528
+ - tracks/swift/exercises/say/Sources/SayExample.swift
13529
+ - tracks/swift/exercises/say/Tests/LinuxMain.swift
13530
+ - tracks/swift/exercises/say/Tests/SayTests/SayTests.swift
13514
13531
  - tracks/swift/exercises/scrabble-score/.gitignore
13515
13532
  - tracks/swift/exercises/scrabble-score/Package.swift
13516
13533
  - tracks/swift/exercises/scrabble-score/README.md