trackler 2.0.6.0 → 2.0.6.1
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/csharp/.gitignore +3 -1
- data/tracks/csharp/README.md +1 -1
- data/tracks/csharp/build.fsx +6 -0
- data/tracks/csharp/circle.yml +16 -0
- data/tracks/csharp/paket.dependencies +2 -1
- data/tracks/csharp/paket.lock +3 -0
- data/tracks/ecmascript/exercises/phone-number/example.js +15 -28
- data/tracks/ecmascript/exercises/phone-number/phone-number.spec.js +28 -13
- data/tracks/fsharp/.gitignore +3 -1
- data/tracks/fsharp/README.md +1 -1
- data/tracks/fsharp/build.fsx +7 -1
- data/tracks/fsharp/circle.yml +16 -0
- data/tracks/fsharp/paket.dependencies +2 -1
- data/tracks/fsharp/paket.lock +3 -0
- data/tracks/ocaml/tools/test-generator/src/controller.ml +5 -5
- data/tracks/ocaml/tools/test-generator/src/model.ml +2 -0
- data/tracks/ocaml/tools/test-generator/src/parser.ml +35 -31
- data/tracks/ocaml/tools/test-generator/src/special_cases.ml +27 -23
- data/tracks/ocaml/tools/test-generator/src/utils.ml +2 -2
- data/tracks/ocaml/tools/test-generator/templates/dominoes/template.ml +40 -0
- data/tracks/ocaml/tools/test-generator/test/parser_test.ml +27 -21
- data/tracks/pony/config.json +5 -0
- data/tracks/pony/docs/ABOUT.md +12 -0
- data/tracks/pony/docs/INSTALLATION.md +4 -6
- data/tracks/pony/docs/LEARNING.md +7 -0
- data/tracks/pony/docs/RESOURCES.md +7 -0
- data/tracks/pony/docs/TESTS.md +11 -0
- data/tracks/pony/exercises/hamming/test.pony +2 -2
- data/tracks/pony/exercises/rna-transcription/example.pony +13 -0
- data/tracks/pony/exercises/rna-transcription/test.pony +32 -0
- data/tracks/r/exercises/anagram/example.R +18 -2
- data/tracks/r/exercises/bob/example.R +16 -1
- data/tracks/r/exercises/luhn/example.R +23 -7
- data/tracks/r/exercises/luhn/luhn.R +6 -0
- data/tracks/r/exercises/word-count/example.R +5 -0
- data/tracks/ruby/exercises/series/example.rb +12 -22
- data/tracks/ruby/exercises/series/series_test.rb +19 -12
- data/tracks/ruby/test/test_helper.rb +3 -3
- data/tracks/scala/config.json +7 -0
- data/tracks/scala/exercises/sieve/build.sbt +2 -2
- data/tracks/scala/exercises/simple-cipher/build.sbt +2 -2
- metadata +10 -1
@@ -3,18 +3,18 @@ open Core.Std
|
|
3
3
|
open Model
|
4
4
|
|
5
5
|
let optional_int ~(none: int) = function
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
| Int n when n = none -> "None"
|
7
|
+
| Int n -> "(Some " ^ Int.to_string n ^ ")"
|
8
|
+
| x -> parameter_to_string x
|
9
9
|
|
10
10
|
let optional_int_list = function
|
11
|
-
|
12
|
-
|
11
|
+
| IntList xs -> "(Some [" ^ String.concat ~sep:"; " (List.map ~f:Int.to_string xs) ^ "])"
|
12
|
+
| _ -> "None"
|
13
13
|
|
14
14
|
let optional_int_or_string ~(none: int) = function
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
| String s -> "(Some \"" ^ s ^ "\")"
|
16
|
+
| Int n when n = none -> "None"
|
17
|
+
| x -> parameter_to_string x
|
18
18
|
|
19
19
|
let default_value ~(key: string) ~(value: string) (parameters: (string * string) list): (string * string) list =
|
20
20
|
if List.exists ~f:(fun (k, _) -> k = key) parameters
|
@@ -30,28 +30,32 @@ let optional_strings ~(f: string -> bool) (parameters: (string * string) list):
|
|
30
30
|
List.map ~f:replace parameters
|
31
31
|
|
32
32
|
let edit_expected ~(stringify: parameter -> string) ~(slug: string) ~(value: parameter) = match slug with
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
| "hamming" -> optional_int ~none:(-1) value
|
34
|
+
| "all-your-base" -> optional_int_list value
|
35
|
+
| "say" -> optional_int_or_string ~none:(-1) value
|
36
|
+
| _ -> stringify value
|
37
37
|
|
38
38
|
let edit_say (ps: (string * string) list) =
|
39
39
|
let edit = function
|
40
|
-
|
41
|
-
|
40
|
+
| ("input", v) -> ("input", if Int.of_string v >= 0 then "(" ^ v ^ "L)" else v ^ "L")
|
41
|
+
| x -> x in
|
42
42
|
List.map ps ~f:edit
|
43
43
|
|
44
44
|
let edit_all_your_base (ps: (string * string) list) =
|
45
45
|
let edit = function
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
| ("output_base", v) -> ("output_base", if Int.of_string v >= 0 then v else "(" ^ v ^ ")")
|
47
|
+
| ("input_base", v) -> ("input_base", if Int.of_string v >= 0 then v else "(" ^ v ^ ")")
|
48
|
+
| x -> x in
|
49
49
|
List.map ps ~f:edit
|
50
50
|
|
51
51
|
let edit_parameters ~(slug: string) (parameters: (string * string) list) = match (slug, parameters) with
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
52
|
+
| ("hello-world", ps) -> default_value ~key:"name" ~value:"None"
|
53
|
+
@@ optional_strings ~f:(fun _x -> true)
|
54
|
+
@@ parameters
|
55
|
+
| ("say", ps) -> edit_say ps
|
56
|
+
| ("all-your-base", ps) -> edit_all_your_base ps
|
57
|
+
| (_, ps) -> ps
|
58
|
+
|
59
|
+
let expected_key_name slug = match slug with
|
60
|
+
| "dominoes" -> "can_chain"
|
61
|
+
| _ -> "expected"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open OUnit2
|
3
|
+
open Dominoes
|
4
|
+
|
5
|
+
let print_dominoe (d1, d2) = sprintf "(%d,%d)" d1 d2
|
6
|
+
|
7
|
+
let option_printer = function
|
8
|
+
| None -> "None"
|
9
|
+
| Some xs -> "Some [" ^ String.concat ~sep:";" (List.map xs ~f:print_dominoe) ^ "]"
|
10
|
+
|
11
|
+
let drop_1_right xs = List.rev xs |> List.tl_exn |> List.rev
|
12
|
+
|
13
|
+
let check_chain (chained: dominoe list) =
|
14
|
+
let assert_dominoes_match d1 d2 =
|
15
|
+
if snd d1 <> fst d2 then failwith @@ sprintf "%s and %s cannot be chained together" (print_dominoe d1) (print_dominoe d2) else () in
|
16
|
+
let consecutives = List.zip_exn (drop_1_right chained) (List.tl_exn chained) in
|
17
|
+
List.iter consecutives ~f:(fun (d1, d2) -> assert_dominoes_match d1 d2)
|
18
|
+
|
19
|
+
let assert_empty c = if List.is_empty c then () else failwith "Expected 0 length chain"
|
20
|
+
|
21
|
+
let assert_valid_chain input _ctxt =
|
22
|
+
match chain input with
|
23
|
+
| None -> failwith "Expecting a chain"
|
24
|
+
| Some(c) -> (if List.is_empty input then assert_empty else check_chain) c
|
25
|
+
|
26
|
+
let assert_no_chain input _ctxt =
|
27
|
+
assert_equal None (chain input) ~printer:option_printer
|
28
|
+
|
29
|
+
let assert_chain input hasChain =
|
30
|
+
if hasChain then assert_valid_chain input else assert_no_chain input
|
31
|
+
|
32
|
+
let tests = [
|
33
|
+
(* TEST
|
34
|
+
"$description" >::
|
35
|
+
assert_chain $input $expected;
|
36
|
+
END TEST *)
|
37
|
+
]
|
38
|
+
|
39
|
+
let () =
|
40
|
+
run_test_tt_main ("dominoes tests" >::: tests)
|
@@ -12,101 +12,107 @@ let ae exp got _test_ctxt = assert_equal exp got ~printer:show_cases
|
|
12
12
|
|
13
13
|
let single x = Ok (Single x)
|
14
14
|
|
15
|
+
let call_parser json = parse_json_text json "expected"
|
16
|
+
|
15
17
|
let parser_tests = [
|
16
18
|
"parses empty json as empty list" >::
|
17
|
-
ae (single []) (
|
19
|
+
ae (single []) (call_parser "{\"cases\" : []}");
|
18
20
|
|
19
21
|
"gives an error with a json map that does not have the key cases in" >::
|
20
22
|
ae (Error (TestMustHaveKeyCalledCases "case"))
|
21
|
-
(
|
23
|
+
(call_parser "{\"case\" : [{\"description\" : \"d1\", \"expected\" : 100}]}");
|
22
24
|
|
23
25
|
"gives an error with cases that is not a json list" >::
|
24
26
|
ae (Error ExpectingListOfCases)
|
25
|
-
(
|
27
|
+
(call_parser "{\"cases\" : 11}");
|
26
28
|
|
27
29
|
"gives an error with a case that is not a json map" >::
|
28
30
|
ae (Error ExpectingMapForCase)
|
29
|
-
(
|
31
|
+
(call_parser "{\"cases\" : [\"key\"]}");
|
30
32
|
|
31
33
|
"parses a single element with a description and expected string output" >::
|
32
34
|
ae (single [{description = "d1"; parameters = []; expected = String "value"}])
|
33
|
-
(
|
35
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"expected\" : \"value\"}]}");
|
34
36
|
|
35
37
|
"parses a single element with a description and expected float output" >::
|
36
38
|
ae (single [{description = "d1"; parameters = []; expected = Float 100.}])
|
37
|
-
(
|
39
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"expected\" : 100.0}]}");
|
38
40
|
|
39
41
|
"parses a single element with a description and expected bool output" >::
|
40
42
|
ae (single [{description = "d1"; parameters = []; expected = Bool true}])
|
41
|
-
(
|
43
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"expected\" : true}]}");
|
42
44
|
|
43
45
|
"parses a single element with a description and expected int list output" >::
|
44
46
|
ae (single [{description = "d1"; parameters = []; expected = IntList [1;2;3]}])
|
45
|
-
(
|
47
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"expected\" : [1, 2, 3]}]}");
|
46
48
|
|
47
49
|
"parses a single element with a description and expected null output" >::
|
48
50
|
ae (single [{description = "d1"; parameters = []; expected = Null}])
|
49
|
-
(
|
51
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"expected\" : null}]}");
|
50
52
|
|
51
53
|
"parses a single element with an int key value pair" >::
|
52
54
|
ae (single [{description = "d1"; parameters = [("input", Int 1996)]; expected = Bool true}])
|
53
|
-
(
|
55
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"input\" : 1996, \"expected\" : true}]}");
|
54
56
|
|
55
57
|
"parses a single element with a string key value pair" >::
|
56
58
|
ae (single [{description = "d1"; parameters = [("input", String "some-string")]; expected = Int 85}])
|
57
|
-
(
|
59
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"input\" : \"some-string\", \"expected\" : 85}]}");
|
58
60
|
|
59
61
|
"parses a single element with a string list key value pair" >::
|
60
62
|
ae (single [{description = "d1"; parameters = [("input", StringList ["s1"; "s2"])]; expected = Int 85}])
|
61
|
-
(
|
63
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"input\" : [\"s1\", \"s2\"], \"expected\" : 85}]}");
|
64
|
+
|
65
|
+
"parses a single element with a int tuple list (modelled as an int list) key value pair" >::
|
66
|
+
ae (single [{description = "d1"; parameters = [("input", IntTupleList [(1,2);(3,4)])]; expected = Int 85}])
|
67
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"input\" : [[1,2],[3,4]], \"expected\" : 85}]}");
|
62
68
|
|
63
69
|
"an element without a description is an Error" >::
|
64
70
|
ae (Error NoDescription)
|
65
|
-
(
|
71
|
+
(call_parser "{\"cases\" : [{\"input\" : 11, \"expected\" : 85}]}");
|
66
72
|
|
67
73
|
"an element with a description which is an int is an Error" >::
|
68
74
|
ae (Error BadDescription)
|
69
|
-
(
|
75
|
+
(call_parser "{\"cases\" : [{\"description\" : 1, \"input\" : 11, \"expected\" : 85}]}");
|
70
76
|
|
71
77
|
"an element without description is an Error" >::
|
72
78
|
ae (Error NoDescription)
|
73
|
-
(
|
79
|
+
(call_parser "{\"cases\" : [{\"input\" : 11}]}");
|
74
80
|
|
75
81
|
"parses a map in the expected parameter" >::(fun _ctx ->
|
76
82
|
assert_equal (single [{description = "d1"; parameters = []; expected = IntStringMap [("one", 1); ("two", 2)]}])
|
77
|
-
(
|
83
|
+
(call_parser "{\"cases\" : [{\"description\" : \"d1\", \"expected\" : {\"one\": 1, \"two\": 2}}]}");
|
78
84
|
);
|
79
85
|
|
80
86
|
"parses leap.json" >::(fun ctxt ->
|
81
|
-
match
|
87
|
+
match call_parser @@ In_channel.read_all "test/leap.json" with
|
82
88
|
| Ok (Single p) -> assert_equal 7 (List.length p)
|
83
89
|
| Ok (Suite p) -> assert_failure "was suite"
|
84
90
|
| Error e -> assert_failure ("failed to parse leap.json: " ^ show_error e)
|
85
91
|
);
|
86
92
|
|
87
93
|
"parses hello_world.json which has a # element as documentation" >::(fun ctxt ->
|
88
|
-
match
|
94
|
+
match call_parser @@ In_channel.read_all "test/hello_world.json" with
|
89
95
|
| Ok (Single p) -> ()
|
90
96
|
| Ok (Suite p) -> assert_failure "was suite"
|
91
97
|
| Error e -> assert_failure ("failed to parse hello_world.json: " ^ show_error e)
|
92
98
|
);
|
93
99
|
|
94
100
|
"parses difference_of_squares.json" >::(fun ctxt ->
|
95
|
-
match
|
101
|
+
match call_parser @@ In_channel.read_all "test/difference_of_squares.json" with
|
96
102
|
| Ok (Suite p) -> assert_equal ["square_of_sum"; "sum_of_squares"; "difference_of_squares"] (List.map ~f:(fun x -> x.name) p)
|
97
103
|
| Ok (Single p) -> assert_failure "was single"
|
98
104
|
| Error e -> assert_failure ("failed to parse difference_of_squares.json: " ^ show_error e)
|
99
105
|
);
|
100
106
|
|
101
107
|
"parses clock.json" >::(fun ctxt ->
|
102
|
-
match
|
108
|
+
match call_parser @@ In_channel.read_all "test/clock.json" with
|
103
109
|
| Ok (Suite p) -> assert_equal ["create"; "add"; "equal"] (List.map ~f:(fun x -> x.name) p)
|
104
110
|
| Ok (Single p) -> assert_failure "was single"
|
105
111
|
| Error e -> assert_failure ("failed to parse clock.json: " ^ show_error e)
|
106
112
|
);
|
107
113
|
|
108
114
|
"parses json with a methods key for dynamic languages" >::(fun ctxt ->
|
109
|
-
match
|
115
|
+
match call_parser @@ In_channel.read_all "test/with-methods-key.json" with
|
110
116
|
| Ok (Suite p) -> assert_failure "was suite"
|
111
117
|
| Ok (Single p) -> ()
|
112
118
|
| Error e -> assert_failure ("failed to parse with-methods-key.json: " ^ show_error e)
|
data/tracks/pony/config.json
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
[Pony](http://www.ponylang.org) is an object-oriented, actor-model, capabilities-secure programming language focused on geting stuff done.
|
2
|
+
|
3
|
+
It's object-oriented because it has classes and objects, like Python, Java, C++, and many other languages. It's actor-model because it has actors (similar to Erlang or Akka). These behave like objects, but they can also execute code asynchronously. Actors make Pony awesome.
|
4
|
+
When we say Pony is capabilities-secure, we mean a few things:
|
5
|
+
|
6
|
+
- It's type safe. Really type safe. There's a mathematical proof and everything.
|
7
|
+
- It's memory safe. Ok, this comes with type safe, but it's still interesting. There are no dangling pointers, no buffer overruns, heck, the language doesn't even have the concept of null!
|
8
|
+
- It's exception safe. There are no runtime exceptions. All exceptions have defined semantics, and they are always handled.
|
9
|
+
- It's data-race free. Pony doesn't have locks or atomic operations or anything like that. Instead, the type system ensures at compile time that your concurrent program can never have data races. So you can write highly concurrent code and never get it wrong.
|
10
|
+
- It's deadlock free. This one is easy, because Pony has no locks at all! So they definitely don't deadlock, because they don't exist.
|
11
|
+
|
12
|
+
Newcomers should start with the [tutorial](https://tutorial.ponylang.org/).
|
@@ -1,14 +1,12 @@
|
|
1
|
-
##
|
1
|
+
## Installing the Pony Compiler
|
2
2
|
|
3
|
-
### Mac OS X
|
4
|
-
|
5
|
-
The Pony compiler (ponyc) is available for install using Homebrew!
|
3
|
+
### Homebrew on Mac OS X
|
6
4
|
|
7
5
|
```bash
|
8
6
|
brew update
|
9
7
|
brew install ponyc
|
10
8
|
```
|
11
9
|
|
12
|
-
|
10
|
+
### Other Platforms
|
13
11
|
|
14
|
-
Instructions for installing Pony
|
12
|
+
Instructions for installing Pony can be found in the [ponyc installation](https://github.com/ponylang/ponyc/blob/master/README.md#installation)
|
@@ -0,0 +1,7 @@
|
|
1
|
+
## Resources for Learning Pony:
|
2
|
+
|
3
|
+
- [The Pony Tutorial](https://tutorial.ponylang.org/)
|
4
|
+
- [Pony Patterns](https://patterns.ponylang.org/)
|
5
|
+
- [Standard Library Documentation](http://www.ponylang.org/ponyc/)
|
6
|
+
- [Mailing List](https://pony.groups.io/g/pony)
|
7
|
+
- [IRC Channel](https://webchat.freenode.net/?channels=%23ponylang)
|
@@ -0,0 +1,7 @@
|
|
1
|
+
## Resources for Learning Pony
|
2
|
+
|
3
|
+
- [The Pony Tutorial](https://tutorial.ponylang.org/)
|
4
|
+
- [Pony Patterns](https://patterns.ponylang.org/)
|
5
|
+
- [Standard Library Documentation](http://www.ponylang.org/ponyc/)
|
6
|
+
- [Mailing List](https://pony.groups.io/g/pony)
|
7
|
+
- [IRC Channel](https://webchat.freenode.net/?channels=%23ponylang)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
## Running tests
|
2
|
+
|
3
|
+
To compile and run the tests, just run the following in your exercise directory:
|
4
|
+
```bash
|
5
|
+
$ ponyc
|
6
|
+
$ ./exercise-name
|
7
|
+
```
|
8
|
+
|
9
|
+
(Replace `exercise-name` with the name of the exercise directory)
|
10
|
+
|
11
|
+
The compiled binary runs the tests in its Main actor, so just run the binary and see if your tests passed. Pony checks all code at compile time, so you may find that your tests won't compile until you write the required code. You can comment out tests that won't compile by starting each line with a `//`. Then, when you're ready to work on that test, you can un-comment it.
|
@@ -36,5 +36,5 @@ class iso _HammingDistanceTest is UnitTest
|
|
36
36
|
fun name(): String => "hamming/Hamming"
|
37
37
|
|
38
38
|
fun apply(h: TestHelper) =>
|
39
|
-
h.assert_error(
|
40
|
-
h.assert_error(
|
39
|
+
h.assert_error({()? => Hamming("GAT", "GA")})
|
40
|
+
h.assert_error({()? => Hamming("GA", "GAC")})
|
@@ -0,0 +1,32 @@
|
|
1
|
+
use "ponytest"
|
2
|
+
|
3
|
+
actor Main is TestList
|
4
|
+
new create(env: Env) =>
|
5
|
+
PonyTest(env, this)
|
6
|
+
|
7
|
+
fun tag tests(test: PonyTest) =>
|
8
|
+
test(_TestRNATrancription)
|
9
|
+
|
10
|
+
class iso _TestRNATrancription is UnitTest
|
11
|
+
fun name(): String => "rna-transcription"
|
12
|
+
|
13
|
+
fun apply(h: TestHelper) ? =>
|
14
|
+
let tests = [
|
15
|
+
// rna complement of cytosine is guanine
|
16
|
+
("C", "G"),
|
17
|
+
// rna complement of guanine is cytosine
|
18
|
+
("G", "C"),
|
19
|
+
// rna complement of thymine is adenine
|
20
|
+
("T", "A"),
|
21
|
+
// rna complement of adenine is uracil
|
22
|
+
("A", "U"),
|
23
|
+
// rna complement
|
24
|
+
("ACGTGGTCTTAA", "UGCACCAGAAUU")
|
25
|
+
]
|
26
|
+
|
27
|
+
for (input, expected) in tests.values() do
|
28
|
+
h.assert_eq[String](ToRNA(input), expected)
|
29
|
+
end
|
30
|
+
|
31
|
+
h.assert_error({() ? => ToRNA("U")})
|
32
|
+
h.assert_error({() ? => ToRNA("ACGTXXXCTTAA")})
|
@@ -1,3 +1,19 @@
|
|
1
|
-
anagram <- function() {
|
2
|
-
|
1
|
+
anagram <- function(subject,candidates) {
|
2
|
+
# subject is a string
|
3
|
+
# candidates is a character vector
|
4
|
+
# break subject into vector of characters
|
5
|
+
subject <- tolower(subject)
|
6
|
+
test_candidates <- tolower(candidates)
|
7
|
+
l <- strsplit(subject,"")[[1]]
|
8
|
+
# sub out each character from l in candidates
|
9
|
+
for (i in l) {
|
10
|
+
test_candidates <- sub(i,"",test_candidates)
|
11
|
+
}
|
12
|
+
result <- candidates[test_candidates=="" &
|
13
|
+
tolower(candidates) != subject &
|
14
|
+
nchar(candidates) == length(l)]
|
15
|
+
if (length(result)==0) {
|
16
|
+
return(c())
|
17
|
+
}
|
18
|
+
return(result)
|
3
19
|
}
|
@@ -1,3 +1,18 @@
|
|
1
|
-
bob <- function() {
|
1
|
+
bob <- function(input) {
|
2
|
+
# function bob takes a character input and responds
|
3
|
+
# with a couple different sentences
|
2
4
|
|
5
|
+
# strip white space
|
6
|
+
input <- gsub("[[:space:]]","",input)
|
7
|
+
|
8
|
+
if (input == "") {
|
9
|
+
return("Fine. Be that way!")
|
10
|
+
}
|
11
|
+
if (toupper(input) == input && tolower(input) != input) {
|
12
|
+
return("Whoa, chill out!")
|
13
|
+
}
|
14
|
+
if (endsWith(input,"?")) {
|
15
|
+
return("Sure.")
|
16
|
+
}
|
17
|
+
return("Whatever.")
|
3
18
|
}
|
@@ -1,19 +1,35 @@
|
|
1
1
|
# Convert a number to a valid number, if it isn't already.
|
2
|
-
luhn <- function() {
|
3
|
-
|
2
|
+
luhn <- function(input) {
|
3
|
+
if (is_valid(input)) {
|
4
|
+
return(input)
|
5
|
+
}
|
6
|
+
diff <- (10 - checksum(10*input)) %% 10
|
7
|
+
return(10 * input + diff)
|
4
8
|
}
|
5
9
|
|
6
10
|
# Get the check digit
|
7
|
-
check_digit <- function() {
|
8
|
-
|
11
|
+
check_digit <- function(input) {
|
12
|
+
as.numeric(substring(input,nchar(input)))
|
9
13
|
}
|
10
14
|
|
11
15
|
# Compute the checksum
|
12
|
-
checksum <- function() {
|
13
|
-
|
16
|
+
checksum <- function(input) {
|
17
|
+
sum(addends(input))
|
14
18
|
}
|
15
19
|
|
16
20
|
# Determine whether the number is valid.
|
17
|
-
is_valid <- function() {
|
21
|
+
is_valid <- function(input) {
|
22
|
+
checksum(input) %% 10 == 0
|
23
|
+
}
|
18
24
|
|
25
|
+
# addends returns the vector of numbers that follow the luhn algorithm
|
26
|
+
addends <- function(input) {
|
27
|
+
check <- check_digit(input)
|
28
|
+
v <- as.numeric(strsplit(as.character(input),"")[[1]])
|
29
|
+
# counting from right double value of every second digit
|
30
|
+
start_seq <- ifelse(length(v) %% 2 == 1, 0, 1)
|
31
|
+
v2 <- replace(v,seq(start_seq,length(v),2),v[seq(start_seq,length(v),2)]*2)
|
32
|
+
v2 <- ifelse(v2 > 9, v2 - 9, v2)
|
33
|
+
replace_vals <- v2[seq(start_seq,length(v),2)]
|
34
|
+
replace(v,seq(start_seq,length(v),2),replace_vals)
|
19
35
|
}
|