trackler 2.2.1.75 → 2.2.1.76

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/alphametics/canonical-data.json +18 -1
  4. data/problem-specifications/exercises/anagram/canonical-data.json +72 -39
  5. data/problem-specifications/exercises/binary/canonical-data.json +47 -17
  6. data/tracks/clojure/exercises/beer-song/src/beer_song.clj +11 -0
  7. data/tracks/erlang/config.json +10 -0
  8. data/tracks/erlang/exercises/raindrops/README.md +67 -0
  9. data/tracks/erlang/exercises/raindrops/rebar.config +30 -0
  10. data/tracks/erlang/exercises/raindrops/src/example.erl +30 -0
  11. data/tracks/erlang/exercises/raindrops/src/raindrops.app.src +9 -0
  12. data/tracks/erlang/exercises/raindrops/src/raindrops.erl +8 -0
  13. data/tracks/erlang/exercises/raindrops/test/raindrops_tests.erl +50 -0
  14. data/tracks/fsharp/exercises/bob/BobTest.fs +2 -2
  15. data/tracks/fsharp/exercises/bob/Example.fs +10 -4
  16. data/tracks/fsharp/exercises/book-store/BookStoreTest.fs +5 -1
  17. data/tracks/fsharp/exercises/rna-transcription/RnaTranscriptionTest.fs +1 -13
  18. data/tracks/fsharp/generators/CanonicalData.fs +14 -7
  19. data/tracks/fsharp/generators/Exercise.fs +84 -18
  20. data/tracks/fsharp/generators/Generators.fs +65 -62
  21. data/tracks/fsharp/generators/Generators.fsproj +0 -4
  22. data/tracks/fsharp/generators/Options.fs +51 -17
  23. data/tracks/fsharp/generators/Program.fs +34 -7
  24. data/tracks/fsharp/generators/Rendering.fs +2 -1
  25. data/tracks/go/config.json +11 -0
  26. data/tracks/go/exercises/reverse-string/.meta/gen.go +52 -0
  27. data/tracks/go/exercises/reverse-string/README.md +31 -0
  28. data/tracks/go/exercises/reverse-string/cases_test.go +37 -0
  29. data/tracks/go/exercises/reverse-string/example.go +10 -0
  30. data/tracks/go/exercises/reverse-string/reverse_string_test.go +25 -0
  31. data/tracks/haskell/exercises/bob/README.md +2 -0
  32. data/tracks/haskell/exercises/bob/examples/success-standard/src/Bob.hs +7 -3
  33. data/tracks/haskell/exercises/bob/package.yaml +1 -1
  34. data/tracks/haskell/exercises/bob/test/Tests.hs +1 -1
  35. data/tracks/haskell/exercises/isbn-verifier/README.md +25 -20
  36. data/tracks/haskell/exercises/pov/README.md +0 -2
  37. data/tracks/haskell/exercises/secret-handshake/README.md +1 -1
  38. data/tracks/haskell/exercises/simple-cipher/README.md +4 -6
  39. data/tracks/java/config.json +12 -0
  40. data/tracks/java/exercises/beer-song/README.md +1 -1
  41. data/tracks/java/exercises/house/README.md +1 -1
  42. data/tracks/java/exercises/isbn-verifier/README.md +27 -21
  43. data/tracks/java/exercises/kindergarten-garden/README.md +3 -3
  44. data/tracks/java/exercises/meetup/README.md +16 -12
  45. data/tracks/java/exercises/nucleotide-count/README.md +2 -2
  46. data/tracks/java/exercises/palindrome-products/README.md +1 -1
  47. data/tracks/java/exercises/parallel-letter-frequency/.meta/HINTS.md +3 -0
  48. data/tracks/java/exercises/parallel-letter-frequency/.meta/src/reference/java/ParallelLetterFrequency.java +45 -0
  49. data/tracks/java/exercises/parallel-letter-frequency/README.md +30 -0
  50. data/tracks/java/exercises/parallel-letter-frequency/build.gradle +18 -0
  51. data/tracks/java/exercises/parallel-letter-frequency/src/main/java/.keep +0 -0
  52. data/tracks/java/exercises/parallel-letter-frequency/src/test/java/ParallelLetterFrequencyTest.java +235 -0
  53. data/tracks/java/exercises/pig-latin/README.md +1 -0
  54. data/tracks/java/exercises/protein-translation/README.md +4 -2
  55. data/tracks/java/exercises/rectangles/README.md +9 -9
  56. data/tracks/java/exercises/settings.gradle +1 -0
  57. data/tracks/java/exercises/simple-cipher/README.md +4 -6
  58. data/tracks/java/exercises/sum-of-multiples/README.md +3 -3
  59. data/tracks/objective-c/config.json +11 -0
  60. data/tracks/objective-c/exercises/two-fer/TwoFerExample.h +15 -0
  61. data/tracks/objective-c/exercises/two-fer/TwoFerExample.m +21 -0
  62. data/tracks/objective-c/exercises/two-fer/TwoFerTest.m +31 -0
  63. data/tracks/objective-c/xcodeProject/ObjectiveC.xcodeproj/project.pbxproj +18 -0
  64. data/tracks/rust/exercises/bob/Cargo.toml +1 -1
  65. data/tracks/rust/exercises/bob/README.md +2 -0
  66. data/tracks/rust/exercises/bob/example.rs +1 -0
  67. data/tracks/rust/exercises/bob/tests/bob.rs +1 -1
  68. data/tracks/rust/exercises/isbn-verifier/README.md +25 -20
  69. data/tracks/typescript/config.json +13 -0
  70. data/tracks/typescript/exercises/atbash-cipher/README.md +60 -0
  71. data/tracks/typescript/exercises/atbash-cipher/atbash-cipher.example.ts +32 -0
  72. data/tracks/typescript/exercises/atbash-cipher/atbash-cipher.test.ts +73 -0
  73. data/tracks/typescript/exercises/atbash-cipher/atbash-cipher.ts +0 -0
  74. data/tracks/typescript/exercises/atbash-cipher/package.json +36 -0
  75. data/tracks/typescript/exercises/atbash-cipher/tsconfig.json +22 -0
  76. data/tracks/typescript/exercises/atbash-cipher/tslint.json +127 -0
  77. data/tracks/typescript/exercises/atbash-cipher/yarn.lock +2624 -0
  78. metadata +31 -2
@@ -16,10 +16,6 @@
16
16
  <Compile Include="Program.fs" />
17
17
  </ItemGroup>
18
18
 
19
- <ItemGroup>
20
- <EmbeddedResource Include="Templates\*.liquid" />
21
- </ItemGroup>
22
-
23
19
  <ItemGroup>
24
20
  <PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
25
21
  <PackageReference Include="DotLiquid" Version="2.0.232" />
@@ -4,26 +4,60 @@ open System
4
4
  open System.IO
5
5
  open CommandLine
6
6
 
7
- type Options = {
8
- [<Option('e', "exercise", Required = false,
9
- HelpText = "Exercise to generate (if not specified, defaults to all exercises).")>] Exercise : string;
10
- [<Option('d', "canonicaldatadirectory", Required = false,
11
- HelpText = "Canonical data directory. If the directory does not exist, the canonical data will be downloaded.")>] CanonicalDataDirectory : string;
12
- [<Option('s', "skipupdatecanonicaldata", Required = false,
13
- HelpText = "Don't update the canonical data.")>] SkipUpdateCanonicalData : bool;
14
- }
15
-
16
- let private normalizeOptions options =
17
- if options.CanonicalDataDirectory <> "" then
18
- options
7
+ type Status =
8
+ | Implemented
9
+ | Unimplemented
10
+ | MissingData
11
+ | Custom
12
+
13
+ type CommandLineOptions =
14
+ { [<Option('e', "exercise", Required = false,
15
+ HelpText = "Exercise to generate (if not specified, defaults to all exercises).")>] Exercise : string;
16
+ [<Option('s', "status", Required = false,
17
+ HelpText = "The generator status to filter on (defaults to exercises with generator).")>] Status : string;
18
+ [<Option('d', "canonicaldatadirectory", Required = false,
19
+ HelpText = "Canonical data directory. If the directory does not exist, the canonical data will be downloaded.")>] CanonicalDataDirectory : string;
20
+ [<Option('c', "cachecanonicaldata", Required = false,
21
+ HelpText = "Use the cached canonical data and don't update the data.")>] CacheCanonicalData : bool; }
22
+
23
+ type Options =
24
+ { Exercise : string option
25
+ Status : Status option
26
+ CanonicalDataDirectory : string
27
+ CacheCanonicalData : bool }
28
+
29
+ let private normalizeCanonicalDataDirectory canonicalDataDirectory =
30
+ if canonicalDataDirectory <> "" then
31
+ canonicalDataDirectory
19
32
  else
20
33
  let appDataDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
21
34
  let defaultCanonicalDataDirectory = Path.Combine(appDataDirectory, "exercism", "problem-specifications")
22
- { options with CanonicalDataDirectory = defaultCanonicalDataDirectory }
35
+ defaultCanonicalDataDirectory
36
+
37
+ let private normalizeExercise exercise = Option.ofNonEmptyString exercise
38
+
39
+ let private normalizeStatus status =
40
+ match Option.ofNonEmptyString status with
41
+ | Some "Implemented" -> Some Implemented
42
+ | Some "Unimplemented" -> Some Unimplemented
43
+ | Some "MissingData" -> Some MissingData
44
+ | Some "Custom" -> Some Custom
45
+ | Some "All" -> None
46
+ | Some _ -> failwith "Invalid status"
47
+ | None -> Some Implemented
48
+
49
+ let private mapOptions (options: CommandLineOptions) =
50
+ { Exercise = normalizeExercise options.Exercise
51
+ Status = normalizeStatus options.Status
52
+ CanonicalDataDirectory = normalizeCanonicalDataDirectory options.CanonicalDataDirectory
53
+ CacheCanonicalData = options.CacheCanonicalData }
23
54
 
24
55
  let parseOptions argv =
25
- let result = CommandLine.Parser.Default.ParseArguments<Options>(argv)
56
+ let result = CommandLine.Parser.Default.ParseArguments<CommandLineOptions>(argv)
26
57
  match result with
27
- | :? Parsed<Options> as parsed -> Result.Ok(normalizeOptions parsed.Value)
28
- | :? NotParsed<Options> as notParsed -> Result.Error(notParsed.Errors |> Seq.map string)
29
- | _ -> Result.Error(Seq.singleton "Invalid parsing result")
58
+ | :? Parsed<CommandLineOptions> as parsed ->
59
+ Result.Ok(mapOptions parsed.Value)
60
+ | :? NotParsed<CommandLineOptions> as notParsed ->
61
+ Result.Error(notParsed.Errors |> Seq.map string)
62
+ | _ ->
63
+ failwith "Invalid parsing result"
@@ -5,21 +5,48 @@ open Exercise
5
5
  open CanonicalData
6
6
  open Options
7
7
 
8
+ let private isNotFilteredByName options (exercise: Exercise) =
9
+ match options.Exercise with
10
+ | Some filteredExerciseName -> filteredExerciseName = exerciseName exercise
11
+ | None -> true
12
+
13
+ let private isNotFilteredByStatus options (exercise: Exercise) =
14
+ match options.Status, exercise with
15
+ | None, _ -> true
16
+ | Some Status.Implemented, Exercise.Generator _ -> true
17
+ | Some Status.Unimplemented, Exercise.Unimplemented _ -> true
18
+ | Some Status.MissingData, Exercise.MissingData _ -> true
19
+ | Some Status.Custom, Exercise.Custom _ -> true
20
+ | _ -> false
21
+
22
+ let private shouldBeIncluded options (exercise: Exercise) =
23
+ isNotFilteredByName options exercise &&
24
+ isNotFilteredByStatus options exercise
25
+
8
26
  let private regenerateTestClass options =
9
27
  let parseCanonicalData' = parseCanonicalData options
10
28
 
11
- fun (exercise: Exercise) ->
12
- let canonicalData = parseCanonicalData' exercise.Name
13
- exercise.Regenerate(canonicalData)
29
+ fun (exercise) ->
30
+ match exercise with
31
+ | Exercise.Custom custom ->
32
+ Log.Information("{Exercise}: has customized tests", custom.Name)
33
+ | Exercise.Unimplemented unimplemented ->
34
+ Log.Error("{Exercise}: missing test generator", unimplemented.Name)
35
+ | Exercise.MissingData missingData ->
36
+ Log.Warning("{Exercise}: missing canonical data", missingData.Name)
37
+ | Exercise.Generator generator ->
38
+ let canonicalData = parseCanonicalData' generator.Name
39
+ generator.Regenerate(canonicalData)
40
+ Log.Information("{Exercise}: tests generated", generator.Name)
14
41
 
15
42
  let private regenerateTestClasses options =
16
43
  Log.Information("Re-generating test classes...")
17
44
 
18
45
  let regenerateTestClass' = regenerateTestClass options
19
- let filteredExercise = Option.ofNonEmptyString options.Exercise
20
-
21
- createExercises filteredExercise
22
- |> Seq.iter regenerateTestClass'
46
+
47
+ createExercises options
48
+ |> List.filter (shouldBeIncluded options)
49
+ |> List.iter regenerateTestClass'
23
50
 
24
51
  Log.Information("Re-generated test classes.")
25
52
 
@@ -1,5 +1,6 @@
1
1
  module Generators.Rendering
2
2
 
3
+ open System.IO
3
4
  open System.Collections.Generic
4
5
  open System.Reflection
5
6
  open FSharp.Reflection
@@ -12,7 +13,7 @@ type OutputFilter() =
12
13
 
13
14
  static member Indent (input: string) = indent 1 input
14
15
 
15
- let private fileSystem = EmbeddedFileSystem(Assembly.GetExecutingAssembly(), "Generators.Templates")
16
+ let private fileSystem = LocalFileSystem(Path.GetFullPath("./Templates"))
16
17
  Template.RegisterFilter(OutputFilter().GetType())
17
18
  Template.FileSystem <- fileSystem :> DotLiquid.FileSystems.IFileSystem
18
19
 
@@ -115,6 +115,17 @@
115
115
  "unlocked_by": null,
116
116
  "uuid": "c2064df8-cce5-4f03-9d85-0818b4e34112"
117
117
  },
118
+ {
119
+ "core": false,
120
+ "difficulty": 2,
121
+ "slug": "reverse-string",
122
+ "topics": [
123
+ "sequences",
124
+ "strings"
125
+ ],
126
+ "unlocked_by": "isogram",
127
+ "uuid": "e2df2756-7a48-4b45-b601-91be91027dbd"
128
+ },
118
129
  {
119
130
  "core": true,
120
131
  "difficulty": 2,
@@ -0,0 +1,52 @@
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("reverse-string", &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
+ Exercise string
24
+ Version string
25
+ Cases []oneCase
26
+ }
27
+
28
+ // Test cases
29
+ type oneCase struct {
30
+ Description string
31
+ Property string
32
+ Input string
33
+ Expected string
34
+ }
35
+
36
+ // Template to generate test cases.
37
+ var tmpl = `package reverse
38
+
39
+ {{.Header}}
40
+
41
+ var testCases = []struct {
42
+ description string
43
+ input string
44
+ expected string
45
+ }{ {{range .J.Cases}}
46
+ {
47
+ description: {{printf "%q" .Description}},
48
+ input: {{printf "%q" .Input}},
49
+ expected: {{printf "%q" .Expected}},
50
+ },{{end}}
51
+ }
52
+ `
@@ -0,0 +1,31 @@
1
+ # reverse-string
2
+
3
+ Reverse a string
4
+
5
+ For example:
6
+ input: "cool"
7
+ output: "looc"
8
+
9
+ ## Running the tests
10
+
11
+ To run the tests run the command `go test` from within the exercise directory.
12
+
13
+ If the test suite contains benchmarks, you can run these with the `-bench`
14
+ flag:
15
+
16
+ go test -bench .
17
+
18
+ Keep in mind that each reviewer will run benchmarks on a different machine, with
19
+ different specs, so the results from these benchmark tests may vary.
20
+
21
+ ## Further information
22
+
23
+ For more detailed information about the Go track, including how to get help if
24
+ you're having trouble, please visit the exercism.io [Go language page](http://exercism.io/languages/go/about).
25
+
26
+ ## Source
27
+
28
+ Introductory challenge to reverse an input string [https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb](https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb)
29
+
30
+ ## Submitting Incomplete Solutions
31
+ It's possible to submit an incomplete solution so you can see how others have completed the exercise.
@@ -0,0 +1,37 @@
1
+ package reverse
2
+
3
+ // Source: exercism/problem-specifications
4
+ // Commit: ae82c90 More consistent reverse-string canonical data
5
+ // Problem Specifications Version: 1.0.1
6
+
7
+ var testCases = []struct {
8
+ description string
9
+ input string
10
+ expected string
11
+ }{
12
+ {
13
+ description: "an empty string",
14
+ input: "",
15
+ expected: "",
16
+ },
17
+ {
18
+ description: "a word",
19
+ input: "robot",
20
+ expected: "tobor",
21
+ },
22
+ {
23
+ description: "a capitalized word",
24
+ input: "Ramen",
25
+ expected: "nemaR",
26
+ },
27
+ {
28
+ description: "a sentence with punctuation",
29
+ input: "I'm hungry!",
30
+ expected: "!yrgnuh m'I",
31
+ },
32
+ {
33
+ description: "a palindrome",
34
+ input: "racecar",
35
+ expected: "racecar",
36
+ },
37
+ }
@@ -0,0 +1,10 @@
1
+ package reverse
2
+
3
+ // String reverses given string
4
+ func String(s string) string {
5
+ runes := []rune(s)
6
+ for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
7
+ runes[i], runes[j] = runes[j], runes[i]
8
+ }
9
+ return string(runes)
10
+ }
@@ -0,0 +1,25 @@
1
+ package reverse
2
+
3
+ import (
4
+ "testing"
5
+ "testing/quick"
6
+ )
7
+
8
+ func TestReverse(t *testing.T) {
9
+ for _, testCase := range testCases {
10
+ if res := String(testCase.input); res != testCase.expected {
11
+ t.Fatalf("FAIL: %s(%s)\nExpected: %q\nActual: %q",
12
+ testCase.description, testCase.input, testCase.expected, res)
13
+ }
14
+ t.Logf("PASS: %s", testCase.description)
15
+ }
16
+ }
17
+
18
+ func TestReverseOfReverse(t *testing.T) {
19
+ assertion := func(s string) bool {
20
+ return s == String(String(s))
21
+ }
22
+ if err := quick.Check(assertion, nil); err != nil {
23
+ t.Fatal(err)
24
+ }
25
+ }
@@ -6,6 +6,8 @@ Bob answers 'Sure.' if you ask him a question.
6
6
 
7
7
  He answers 'Whoa, chill out!' if you yell at him.
8
8
 
9
+ He answers 'Calm down, I know what I'm doing!' if you yell a question at him.
10
+
9
11
  He says 'Fine. Be that way!' if you address him without actually saying
10
12
  anything.
11
13
 
@@ -1,16 +1,20 @@
1
1
  module Bob (responseFor) where
2
2
  import Data.Char (isSpace, isUpper, isAlpha)
3
3
 
4
- data Prompt = Silence | Yell | Question | Other
4
+ data Prompt = Silence | YellQuestion | Yell | Question | Other
5
5
 
6
6
  classify :: String -> Prompt
7
7
  classify s | all isSpace s = Silence
8
- | any isAlpha s && all isUpper (filter isAlpha s) = Yell
9
- | '?' == last (filter (not . isSpace) s) = Question
8
+ | yell && question = YellQuestion
9
+ | yell = Yell
10
+ | question = Question
10
11
  | otherwise = Other
12
+ where yell = any isAlpha s && all isUpper (filter isAlpha s)
13
+ question = '?' == last (filter (not . isSpace) s)
11
14
 
12
15
  response :: Prompt -> String
13
16
  response Silence = "Fine. Be that way!"
17
+ response YellQuestion = "Calm down, I know what I'm doing!"
14
18
  response Yell = "Whoa, chill out!"
15
19
  response Question = "Sure."
16
20
  response Other = "Whatever."
@@ -1,5 +1,5 @@
1
1
  name: bob
2
- version: 1.0.0.3
2
+ version: 1.1.0.4
3
3
 
4
4
  dependencies:
5
5
  - base
@@ -54,7 +54,7 @@ cases = [ Case { description = "stating something"
54
54
  }
55
55
  , Case { description = "forceful question"
56
56
  , input = "WHAT THE HELL WERE YOU THINKING?"
57
- , expected = "Whoa, chill out!"
57
+ , expected = "Calm down, I know what I'm doing!"
58
58
  }
59
59
  , Case { description = "shouting numbers"
60
60
  , input = "1, 2, 3 GO!"
@@ -1,40 +1,45 @@
1
1
  # Isbn Verifier
2
2
 
3
- Check if a given ISBN-10 is valid.
3
+ The [ISBN-10 verification process](https://en.wikipedia.org/wiki/International_Standard_Book_Number) is used to validate book identification
4
+ numbers. These normally contain dashes and look like: `3-598-21508-8`
4
5
 
5
- ## Functionality
6
+ ## ISBN
6
7
 
7
- Given an unknown string the program should check if the provided string is a valid ISBN-10.
8
- Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN.
8
+ The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only). In the case the check character is an X, this represents the value '10'. These may be communicated with or without hyphens, and can be checked for their validity by the following formula:
9
9
 
10
- The program should allow for ISBN-10 without the separating dashes to be verified as well.
10
+ ```
11
+ (x1 * 10 + x2 * 9 + x3 * 8 + x4 * 7 + x5 * 6 + x6 * 5 + x7 * 4 + x8 * 3 + x9 * 2 + x10 * 1) mod 11 == 0
12
+ ```
11
13
 
12
- ## ISBN
14
+ If the result is 0, then it is a valid ISBN-10, otherwise it is invalid.
13
15
 
14
- Let's take a random ISBN-10 number, say `3-598-21508-8` for this.
15
- 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.
16
- 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.
17
- The last digit in the ISBN is the check digit which is used to detect read errors.
16
+ ## Example
18
17
 
19
- The first 9 digits in the ISBN have to be between 0 and 9.
20
- The check digit can additionally be an 'X' to allow 10 to be a valid check digit as well.
18
+ Let's take the ISBN-10 `3-598-21508-8`. We plug it in to the formula, and get:
19
+ ```
20
+ (3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0
21
+ ```
22
+
23
+ Since the result is 0, this proves that our ISBN is valid.
24
+
25
+ ## Task
26
+
27
+ Given a string the program should check if the provided string is a valid ISBN-10.
28
+ Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN.
21
29
 
22
- 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`
23
- So for our example ISBN this means:
24
- (3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 = 0
30
+ The program should be able to verify ISBN-10 both with and without separating dashes.
25
31
 
26
- Which proves that the ISBN is valid.
27
32
 
28
33
  ## Caveats
29
34
 
30
- Converting from string to number can be tricky in certain languages.
31
- It's getting even trickier since the check-digit of an ISBN-10 can be 'X'.
35
+ Converting from strings to numbers can be tricky in certain languages.
36
+ Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10'). For instance `3-598-21507-X` is a valid ISBN-10.
32
37
 
33
38
  ## Bonus tasks
34
39
 
35
- * Generate a valid ISBN-13 from the input ISBN-10 (and maybe verify it again with a derived verifier)
40
+ * Generate a valid ISBN-13 from the input ISBN-10 (and maybe verify it again with a derived verifier).
36
41
 
37
- * Generate valid ISBN, maybe even from a given starting ISBN
42
+ * Generate valid ISBN, maybe even from a given starting ISBN.
38
43
 
39
44
  ## Getting Started
40
45