trackler 2.0.6.10 → 2.0.6.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/common/exercises/circular-buffer/canonical-data.json +343 -0
  3. data/common/exercises/luhn/description.md +4 -1
  4. data/common/exercises/secret-handshake/canonical-data.json +67 -0
  5. data/common/exercises/secret-handshake/description.md +6 -8
  6. data/common/exercises/variable-length-quantity/canonical-data.json +147 -0
  7. data/lib/trackler/version.rb +1 -1
  8. data/tracks/ceylon/.gitignore +1 -0
  9. data/tracks/ceylon/.travis.yml +6 -1
  10. data/tracks/ceylon/README.md +143 -4
  11. data/tracks/ceylon/bin/test-all-exercises +13 -0
  12. data/tracks/ceylon/bin/test-exercise +30 -0
  13. data/tracks/ceylon/config.json +33 -3
  14. data/tracks/ceylon/{SETUP.md → exercises/TRACK_HINTS.md} +0 -0
  15. data/tracks/ceylon/exercises/anagram/example/Anagram.ceylon +8 -0
  16. data/tracks/ceylon/exercises/anagram/source/anagram/Anagram.ceylon +3 -0
  17. data/tracks/ceylon/exercises/anagram/source/anagram/AnagramTest.ceylon +47 -0
  18. data/tracks/ceylon/exercises/anagram/source/anagram/module.ceylon +3 -0
  19. data/tracks/ceylon/exercises/bracket-push/example/Brackets.ceylon +18 -0
  20. data/tracks/ceylon/exercises/bracket-push/example/module.ceylon +4 -0
  21. data/tracks/ceylon/exercises/bracket-push/source/bracketpush/Brackets.ceylon +3 -0
  22. data/tracks/ceylon/exercises/bracket-push/source/bracketpush/BracketsTest.ceylon +37 -0
  23. data/tracks/ceylon/exercises/bracket-push/source/bracketpush/module.ceylon +3 -0
  24. data/tracks/ceylon/exercises/leap/example/Leap.ceylon +3 -0
  25. data/tracks/ceylon/exercises/leap/source/leap/Leap.ceylon +3 -0
  26. data/tracks/ceylon/exercises/leap/source/leap/LeapTest.ceylon +10 -0
  27. data/tracks/ceylon/exercises/leap/source/leap/module.ceylon +3 -0
  28. data/tracks/ceylon/exercises/sieve/example/Sieve.ceylon +24 -0
  29. data/tracks/ceylon/exercises/sieve/example/module.ceylon +4 -0
  30. data/tracks/ceylon/exercises/sieve/source/sieve/Sieve.ceylon +3 -0
  31. data/tracks/ceylon/exercises/sieve/source/sieve/SieveTest.ceylon +23 -0
  32. data/tracks/ceylon/exercises/sieve/source/sieve/module.ceylon +3 -0
  33. data/tracks/csharp/.travis.yml +4 -0
  34. data/tracks/elm/SETUP.md +5 -4
  35. data/tracks/fsharp/.travis.yml +4 -0
  36. data/tracks/go/config.json +6 -2
  37. data/tracks/go/exercises/luhn/example.go +5 -4
  38. data/tracks/go/exercises/luhn/luhn_test.go +18 -34
  39. data/tracks/java/exercises/word-count/src/test/java/WordCountTest.java +10 -13
  40. data/tracks/lua/docs/ABOUT.md +1 -1
  41. data/tracks/lua/docs/LEARNING.md +1 -0
  42. data/tracks/lua/docs/RESOURCES.md +6 -1
  43. data/tracks/lua/exercises/flatten-array/flatten-array_spec.lua +11 -0
  44. data/tracks/lua/exercises/luhn/example.lua +11 -27
  45. data/tracks/lua/exercises/luhn/luhn_spec.lua +12 -39
  46. data/tracks/ocaml/exercises/all-your-base/all_your_base.mli +0 -2
  47. data/tracks/ocaml/exercises/bracket-push/bracket_push.mli +0 -2
  48. data/tracks/ocaml/exercises/luhn/example.ml +12 -16
  49. data/tracks/ocaml/exercises/luhn/luhn.mli +1 -2
  50. data/tracks/ocaml/exercises/luhn/test.ml +15 -12
  51. data/tracks/php/config.json +8 -0
  52. data/tracks/php/exercises/grains/example.php +63 -0
  53. data/tracks/php/exercises/grains/grains_test.php +82 -0
  54. data/tracks/pony/config.json +26 -14
  55. data/tracks/pony/exercises/anagram/example.pony +17 -73
  56. data/tracks/pony/exercises/anagram/test.pony +6 -13
  57. data/tracks/pony/exercises/bob/example.pony +24 -24
  58. data/tracks/pony/exercises/bob/test.pony +16 -34
  59. data/tracks/pony/exercises/difference-of-squares/example.pony +13 -25
  60. data/tracks/pony/exercises/difference-of-squares/test.pony +9 -16
  61. data/tracks/pony/exercises/hamming/example.pony +8 -23
  62. data/tracks/pony/exercises/hamming/test.pony +16 -30
  63. data/tracks/pony/exercises/hello-world/example.pony +3 -9
  64. data/tracks/pony/exercises/hello-world/test.pony +2 -11
  65. data/tracks/pony/exercises/leap/example.pony +4 -10
  66. data/tracks/pony/exercises/leap/test.pony +2 -11
  67. data/tracks/ruby/lib/generator/command_line.rb +5 -6
  68. data/tracks/ruby/lib/generator/repository.rb +13 -13
  69. data/tracks/ruby/lib/tasks/exercise_test_tasks.rb +2 -4
  70. data/tracks/ruby/lib/tasks/exercise_tests_runner.rb +6 -8
  71. data/tracks/ruby/test/generator/repository_test.rb +26 -34
  72. data/tracks/rust/README.md +1 -1
  73. data/tracks/scheme/config.json +5 -0
  74. data/tracks/scheme/docs/ABOUT.md +7 -0
  75. data/tracks/scheme/exercises/scrabble-score/example.scm +16 -0
  76. data/tracks/scheme/exercises/scrabble-score/scrabble-score-test.scm +57 -0
  77. data/tracks/scheme/exercises/scrabble-score/scrabble-score.scm +2 -0
  78. metadata +33 -5
  79. data/tracks/ceylon/exercises/.keep +0 -0
  80. data/tracks/ceylon/img/.keep +0 -0
@@ -0,0 +1,3 @@
1
+ module anagram "1.0" {
2
+ import "ceylon.test" "1.3.1";
3
+ }
@@ -0,0 +1,18 @@
1
+ import ceylon.collection { ArrayList }
2
+
3
+ Map<Character, Character> openFor = map {'}' -> '{', ')' -> '(', ']' -> '['};
4
+ Set<Character> opens = set(openFor.items);
5
+
6
+ Boolean balanced(String brackets) {
7
+ value expectedOpens = ArrayList<Character>();
8
+ for (c in brackets) {
9
+ if (opens.contains(c)) {
10
+ expectedOpens.push(c);
11
+ } else if (exists open = openFor[c]) {
12
+ if ((expectedOpens.pop() else '!') != open) {
13
+ return false;
14
+ }
15
+ }
16
+ }
17
+ return expectedOpens.empty;
18
+ }
@@ -0,0 +1,4 @@
1
+ module bracketpush "1.0" {
2
+ import "ceylon.collection" "1.3.1";
3
+ import "ceylon.test" "1.3.1";
4
+ }
@@ -0,0 +1,3 @@
1
+ Boolean balanced(String brackets) {
2
+ return nothing;
3
+ }
@@ -0,0 +1,37 @@
1
+ import ceylon.test { ... }
2
+
3
+ {[String, Boolean]*} cases =>
4
+ {
5
+ // paired square brackets
6
+ ["[]", true],
7
+ // empty string
8
+ ["", true],
9
+ // unpaired brackets
10
+ ["[[", false],
11
+ // wrong ordered brackets
12
+ ["}{", false],
13
+ // paired with whitespace
14
+ ["{ }", true],
15
+ // simple nested brackets
16
+ ["{[]}", true],
17
+ // several paired brackets
18
+ ["{}[]", true],
19
+ // paired and nested brackets
20
+ ["([{}({}[])])", true],
21
+ // unopened closing brackets
22
+ ["{[)][]}", false],
23
+ // unpaired and nested brackets
24
+ ["([{])", false],
25
+ // paired and wrong nested brackets
26
+ ["[({]})", false],
27
+ // math expression
28
+ ["(((185 + 223.85) * 15) - 543)/2", true],
29
+ // complex latex expression
30
+ ["\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)", true]
31
+ };
32
+
33
+ test
34
+ parameters(`value cases`)
35
+ void testBalanced(String brackets, Boolean expected) {
36
+ assertEquals(balanced(brackets), expected);
37
+ }
@@ -0,0 +1,3 @@
1
+ module bracketpush "1.0" {
2
+ import "ceylon.test" "1.3.1";
3
+ }
@@ -0,0 +1,3 @@
1
+ Boolean leapYear(Integer year) {
2
+ return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
3
+ }
@@ -0,0 +1,3 @@
1
+ Boolean leapYear(Integer year) {
2
+ return nothing;
3
+ }
@@ -0,0 +1,10 @@
1
+ import ceylon.test { ... }
2
+
3
+ {[Integer, Boolean]*} cases =>
4
+ {[2015, false], [2016, true], [2100, false], [2000, true]};
5
+
6
+ test
7
+ parameters(`value cases`)
8
+ void testLeapYear(Integer year, Boolean isLeap) {
9
+ assertEquals(leapYear(year), isLeap);
10
+ }
@@ -0,0 +1,3 @@
1
+ module leap "1.0" {
2
+ import "ceylon.test" "1.3.1";
3
+ }
@@ -0,0 +1,24 @@
1
+ import ceylon.numeric.float { sqrt }
2
+
3
+ {Integer*} primesUpTo(Integer max) {
4
+ // if max < 4, sqrt(max) < 2, so 2..sqrt(max) gives decreasing values.
5
+ // Prevent this by just returning the small primes here:
6
+ if (max < 4) {
7
+ return {2, 3}.takeWhile((c) => c <= max);
8
+ }
9
+
10
+ value prime = Array.ofSize(max + 1, true);
11
+ prime[0] = false;
12
+ prime[1] = false;
13
+
14
+ for (i in 2..(sqrt(max.float).integer)) {
15
+ if (!(prime[i] else false)) {
16
+ continue;
17
+ }
18
+
19
+ for (multiple in ((i * i)..max).by(i)) {
20
+ prime[multiple] = false;
21
+ }
22
+ }
23
+ return { for (i -> p in prime.indexed) if (p) i };
24
+ }
@@ -0,0 +1,4 @@
1
+ module sieve "1.0" {
2
+ import "ceylon.numeric" "1.3.1";
3
+ import "ceylon.test" "1.3.1";
4
+ }
@@ -0,0 +1,3 @@
1
+ {Integer*} primesUpTo(Integer max) {
2
+ return nothing;
3
+ }
@@ -0,0 +1,23 @@
1
+ import ceylon.test { ... }
2
+
3
+ {Integer*} cases => {1, 2, 10, 13, 1000};
4
+
5
+ [Integer+] primes1000 = [
6
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
7
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
8
+ 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
9
+ 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
10
+ 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
11
+ 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
12
+ 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607,
13
+ 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
14
+ 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811,
15
+ 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
16
+ 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997
17
+ ];
18
+
19
+ test
20
+ parameters(`value cases`)
21
+ void testPrimesUpto(Integer max) {
22
+ assertEquals([*primesUpTo(max)], [*primes1000.takeWhile((x) => x <= max)]);
23
+ }
@@ -0,0 +1,3 @@
1
+ module sieve "1.0" {
2
+ import "ceylon.test" "1.3.1";
3
+ }
@@ -1,5 +1,9 @@
1
1
  language: csharp
2
2
 
3
+ os:
4
+ - linux
5
+ - osx
6
+
3
7
  script:
4
8
  - ./bin/fetch-configlet
5
9
  - ./bin/configlet .
@@ -1,11 +1,12 @@
1
1
  ## Elm Installation
2
2
 
3
- Refer to the [exercism help page][http://exercism.io/languages/elm] for Elm installation and learning
4
- resources.
3
+ Refer to the [Exercism help page](http://exercism.io/languages/elm) for Elm
4
+ installation and learning resources.
5
5
 
6
6
  ## Writing the Code
7
7
 
8
- The first time you start an exercise, you'll need to ensure you have the appropriate dependancies installed.
8
+ The first time you start an exercise, you'll need to ensure you have the
9
+ appropriate dependencies installed.
9
10
 
10
11
  ```bash
11
12
  $ npm install
@@ -15,4 +16,4 @@ Execute the tests with:
15
16
 
16
17
  ```bash
17
18
  $ npm test
18
- ```
19
+ ```
@@ -1,5 +1,9 @@
1
1
  language: csharp
2
2
 
3
+ os:
4
+ - linux
5
+ - osx
6
+
3
7
  script:
4
8
  - ./bin/fetch-configlet
5
9
  - ./bin/configlet .
@@ -46,9 +46,13 @@
46
46
  ]
47
47
  },
48
48
  {
49
- "difficulty": 1,
49
+ "difficulty": 3,
50
50
  "slug": "clock",
51
- "topics": []
51
+ "topics": [
52
+ "Time",
53
+ "Mathematics",
54
+ "Text formatting"
55
+ ]
52
56
  },
53
57
  {
54
58
  "difficulty": 1,
@@ -1,6 +1,11 @@
1
1
  package luhn
2
2
 
3
+ const testVersion = 1
4
+
3
5
  func Valid(n string) bool {
6
+ if len(n) == 1 {
7
+ return false
8
+ }
4
9
  d := extract(n)
5
10
  if len(d) == 0 {
6
11
  return false
@@ -33,7 +38,3 @@ func check(d []int) int {
33
38
  }
34
39
  return s
35
40
  }
36
-
37
- func AddCheck(raw string) string {
38
- return raw + string('0'+(10-check(extract(raw))%10)%10)
39
- }
@@ -2,41 +2,25 @@ package luhn
2
2
 
3
3
  import "testing"
4
4
 
5
- var validTests = []struct {
6
- n string
7
- ok bool
8
- }{
9
- {"738", false},
10
- {"8739567", true},
11
- {"1111", false},
12
- {"8763", true},
13
- {" ", false},
14
- {"", false},
15
- {"2323 2005 7766 3554", true},
16
- }
5
+ const targetTestVersion = 1
17
6
 
18
- var addTests = []struct{ raw, luhn string }{
19
- {"123", "1230"},
20
- {"873956", "8739567"},
21
- {"837263756", "8372637564"},
22
- {"2323 2005 7766 355", "2323 2005 7766 3554"},
23
- // bonus Unicode cases
24
- // {"2323·2005·7766·355", "2323·2005·7766·3554"},
25
- // {"123", "1230"},
7
+ var testCases = []struct {
8
+ input string
9
+ description string
10
+ ok bool
11
+ }{
12
+ {"1", "single digit strings can not be valid", false},
13
+ {"0", "a single zero is invalid", false},
14
+ {"046 454 286", "valid Canadian SIN", true},
15
+ {"046 454 287", "invalid Canadian SIN", false},
16
+ {"8273 1232 7352 0569", "invalid credit card", false},
17
+ {"827a 1232 7352 0569", "strings that contain non-digits are not valid", false},
26
18
  }
27
19
 
28
20
  func TestValid(t *testing.T) {
29
- for _, test := range validTests {
30
- if ok := Valid(test.n); ok != test.ok {
31
- t.Fatalf("Valid(%s) = %t, want %t.", test.n, ok, test.ok)
32
- }
33
- }
34
- }
35
-
36
- func TestAddCheck(t *testing.T) {
37
- for _, test := range addTests {
38
- if luhn := AddCheck(test.raw); luhn != test.luhn {
39
- t.Fatalf("AddCheck(%s) = %s, want %s.", test.raw, luhn, test.luhn)
21
+ for _, test := range testCases {
22
+ if ok := Valid(test.input); ok != test.ok {
23
+ t.Fatalf("Valid(%s): %s\n\t Expected: %t\n\t Got: %t", test.input, test.description, ok, test.ok)
40
24
  }
41
25
  }
42
26
  }
@@ -47,8 +31,8 @@ func BenchmarkValid(b *testing.B) {
47
31
  }
48
32
  }
49
33
 
50
- func BenchmarkAddCheck(b *testing.B) {
51
- for i := 0; i < b.N; i++ {
52
- AddCheck("2323 2005 7766 355")
34
+ func TestTestVersion(t *testing.T) {
35
+ if testVersion != targetTestVersion {
36
+ t.Errorf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
53
37
  }
54
38
  }
@@ -1,3 +1,4 @@
1
+ import org.junit.Before;
1
2
  import org.junit.Test;
2
3
  import org.junit.Ignore;
3
4
 
@@ -10,13 +11,19 @@ import static org.junit.Assert.*;
10
11
 
11
12
  public class WordCountTest {
12
13
 
13
- private final WordCount wordCount = new WordCount();
14
+ private WordCount wordCount;
15
+ private Map<String, Integer> actualWordCount;
16
+ private Map<String, Integer> expectedWordCount;
17
+
18
+ @Before
19
+ public void setup() {
20
+ wordCount = new WordCount();
21
+ expectedWordCount = new HashMap<String, Integer>();
22
+ }
14
23
 
15
24
 
16
25
  @Test
17
26
  public void countOneWord() {
18
- Map<String, Integer> actualWordCount = new HashMap<String, Integer>();
19
- final Map<String, Integer> expectedWordCount = new HashMap<String, Integer>();
20
27
  expectedWordCount.put("word", 1);
21
28
 
22
29
  actualWordCount = wordCount.phrase("word");
@@ -28,8 +35,6 @@ public class WordCountTest {
28
35
  @Ignore
29
36
  @Test
30
37
  public void countOneOfEach() {
31
- Map<String, Integer> actualWordCount = new HashMap<String, Integer>();
32
- final Map<String, Integer> expectedWordCount = new HashMap<String, Integer>();
33
38
  expectedWordCount.put("one", 1);
34
39
  expectedWordCount.put("of", 1);
35
40
  expectedWordCount.put("each", 1);
@@ -43,8 +48,6 @@ public class WordCountTest {
43
48
  @Ignore
44
49
  @Test
45
50
  public void countMultipleOccurences() {
46
- Map<String, Integer> actualWordCount = new HashMap<String, Integer>();
47
- final Map<String, Integer> expectedWordCount = new HashMap<String, Integer>();
48
51
  expectedWordCount.put("one", 1);
49
52
  expectedWordCount.put("fish", 4);
50
53
  expectedWordCount.put("two", 1);
@@ -60,8 +63,6 @@ public class WordCountTest {
60
63
  @Ignore
61
64
  @Test
62
65
  public void ignorePunctuation() {
63
- Map<String, Integer> actualWordCount = new HashMap<String, Integer>();
64
- final Map<String, Integer> expectedWordCount = new HashMap<String, Integer>();
65
66
  expectedWordCount.put("car", 1);
66
67
  expectedWordCount.put("carpet", 1);
67
68
  expectedWordCount.put("as", 1);
@@ -78,8 +79,6 @@ public class WordCountTest {
78
79
  @Ignore
79
80
  @Test
80
81
  public void includeNumbers() {
81
- Map<String, Integer> actualWordCount = new HashMap<String, Integer>();
82
- final Map<String, Integer> expectedWordCount = new HashMap<String, Integer>();
83
82
  expectedWordCount.put("testing", 2);
84
83
  expectedWordCount.put("1", 1);
85
84
  expectedWordCount.put("2", 1);
@@ -93,8 +92,6 @@ public class WordCountTest {
93
92
  @Ignore
94
93
  @Test
95
94
  public void normalizeCase() {
96
- Map<String, Integer> actualWordCount = new HashMap<String, Integer>();
97
- final Map<String, Integer> expectedWordCount = new HashMap<String, Integer>();
98
95
  expectedWordCount.put("go", 3);
99
96
 
100
97
  actualWordCount = wordCount.phrase("go Go GO");
@@ -4,6 +4,6 @@ Lua supports procedural, object-oriented, functional, data-driven programming an
4
4
 
5
5
  Lua has been used in [many industrial applications](https://sites.google.com/site/marbux/home/where-lua-is-used#8S4UcLlroV5fq8i3WSheIA) with an emphasis on embedded systems and games.
6
6
 
7
- The home page for Lua is [Lua.org](https://www.lua.org/). You can [learn the language in 15 minutes](tylerneylon.com/a/learn-lua/). Enjoy!
7
+ The home page for Lua is [Lua.org](https://www.lua.org/). Enjoy!
8
8
 
9
9
  (Taken from https://www.lua.org/about.html)
@@ -0,0 +1 @@
1
+ Lua is a very small language and you can [learn the basics of the language in 15 minutes](tylerneylon.com/a/learn-lua/). For an exhaustive look at the language, written by the authors of Lua, you can read [Programming in Lua](https://www.lua.org/pil/) (the first edition which covers Lua 5.1 [is available online for free](https://www.lua.org/pil/contents.html)).
@@ -1,13 +1,18 @@
1
1
  There are many great free online resources for Lua including:
2
2
 
3
3
  1. [lua.org][8]
4
- 2. A highly recommended detailed and authoritative introduction to all aspects of Lua programming by Lua's chief architect: [Programming in Lua (2nd edition), by Roberto Ierusalimschy][6]
4
+ 2. A highly recommended detailed and authoritative introduction to all aspects of Lua programming by Lua's chief architect: [Programming in Lua, by Roberto Ierusalimschy][6]
5
5
  3. For an official definition of the Lua language, consult the [Lua 5.1 Reference Manual][7], by R. Ierusalimschy, L. H. de Figueiredo, W. Celes.
6
6
  4. [Lua Style Guide][4]
7
7
  5. [Learn Lua in 15 minutes][5]
8
+ 6. [Some][9] [options][10] [for][11] [linting][12]
8
9
 
9
10
  [4]: https://github.com/Olivine-Labs/lua-style-guide
10
11
  [5]: http://tylerneylon.com/a/learn-lua/
11
12
  [6]: http://www.lua.org/pil/
12
13
  [7]: http://www.lua.org/manual/5.1/
13
14
  [8]: http://www.lua.org
15
+ [9]: https://code.google.com/p/lua-checker/
16
+ [10]: http://lua-users.org/wiki/LuaLint
17
+ [11]: http://lua-users.org/wiki/LuaInspect
18
+ [12]: http://lua-users.org/wiki/DetectingUndefinedVariables
@@ -1,3 +1,14 @@
1
+ --[[-
2
+ Note that because ipairs and the length operator work correctly only for sequences
3
+ in Lua, no arrays with nils are included in these tests. This means that the provided
4
+ example is not included here:
5
+
6
+ input: [1,[2,3,null,4],[null],5]
7
+ output: [1,2,3,4,5]
8
+
9
+ See https://www.lua.org/manual/5.3/manual.html#3.4.7 for more information.
10
+ ]]
11
+
1
12
  local flatten = require 'flatten-array'
2
13
 
3
14
  describe('flatten-array', function()
@@ -1,14 +1,7 @@
1
- local Luhn = {}
2
- Luhn.__index = Luhn
3
-
4
- function Luhn:check_digit()
5
- return tonumber(self.s:sub(-1))
6
- end
7
-
8
- function Luhn:addends()
1
+ local function addends(s)
9
2
  local result = {}
10
- for i = #self.s, 1, -1 do
11
- local digit = tonumber(self.s:sub(i, i))
3
+ for i = #s, 1, -1 do
4
+ local digit = tonumber(s:sub(i, i))
12
5
  if #result % 2 > 0 then digit = digit * 2 end
13
6
  if digit > 9 then digit = digit - 9 end
14
7
  table.insert(result, 1, digit)
@@ -16,28 +9,19 @@ function Luhn:addends()
16
9
  return result
17
10
  end
18
11
 
19
- function Luhn:checksum()
12
+ local function checksum(s)
20
13
  local checksum = 0
21
- for _, addend in ipairs(self:addends()) do
14
+ for _, addend in ipairs(addends(s)) do
22
15
  checksum = checksum + addend
23
16
  end
24
17
  return checksum
25
18
  end
26
19
 
27
- function Luhn:valid()
28
- return self:checksum() % 10 == 0
29
- end
30
-
31
- local function new(s)
32
- return setmetatable({ s = s }, Luhn)
33
- end
34
-
35
- local function create(s)
36
- local checksum = new(s .. '0'):checksum()
37
- return s .. tostring((10 - (checksum % 10)) % 10)
38
- end
39
-
40
20
  return {
41
- new = new,
42
- create = create
21
+ valid = function(s)
22
+ s = s:gsub(' ', '')
23
+ if #s < 2 then return false end
24
+ if s:find('%D') then return false end
25
+ return checksum(s) % 10 == 0
26
+ end
43
27
  }