trackler 2.0.8.54 → 2.0.8.55
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.
- checksums.yaml +4 -4
- data/README.md +7 -0
- data/common/CONTRIBUTING.md +7 -6
- data/fixtures/tracks/animal/config.json +1 -0
- data/fixtures/tracks/animal/exercises/dog/a_test_example_for.animal +1 -0
- data/lib/trackler/implementation.rb +32 -38
- data/lib/trackler/implementations.rb +2 -2
- data/lib/trackler/track.rb +24 -4
- data/lib/trackler/version.rb +1 -1
- data/tracks/elixir/config.json +17 -0
- data/tracks/elixir/exercises/bracket-push/bracket_push_test.exs +34 -19
- data/tracks/elixir/exercises/saddle-points/example.exs +1 -1
- data/tracks/elixir/exercises/saddle-points/saddle_points.exs +1 -1
- data/tracks/elixir/exercises/saddle-points/saddle_points_test.exs +10 -10
- data/tracks/elixir/exercises/simple-cipher/example.exs +80 -0
- data/tracks/elixir/exercises/simple-cipher/simple_cipher.exs +49 -0
- data/tracks/elixir/exercises/simple-cipher/simple_cipher_test.exs +89 -0
- data/tracks/elixir/exercises/tournament/HINTS.md +3 -0
- data/tracks/elixir/exercises/tournament/example.exs +81 -0
- data/tracks/elixir/exercises/tournament/tournament.exs +17 -0
- data/tracks/elixir/exercises/tournament/tournament_test.exs +96 -0
- data/tracks/go/exercises/bob/bob.go +2 -2
- data/tracks/go/exercises/clock/clock.go +1 -8
- data/tracks/go/exercises/gigasecond/gigasecond.go +4 -6
- data/tracks/go/exercises/hamming/hamming.go +0 -1
- data/tracks/go/exercises/raindrops/raindrops.go +2 -1
- data/tracks/go/exercises/triangle/triangle.go +1 -2
- data/tracks/java/bin/journey-test.sh +6 -5
- data/tracks/java/exercises/nth-prime/src/example/java/PrimeCalculator.java +1 -5
- data/tracks/java/exercises/rna-transcription/src/test/java/RnaTranscriptionTest.java +5 -5
- data/tracks/pony/.github/stale.yml +0 -0
- data/tracks/pony/.travis.yml +2 -9
- data/tracks/pony/bin/install-deps +15 -8
- data/tracks/pony/bin/test-exercises +2 -0
- data/tracks/pony/config.json +2 -1
- data/tracks/pony/exercises/anagram/example.pony +1 -1
- data/tracks/pony/exercises/anagram/test.pony +2 -2
- data/tracks/pony/exercises/atbash-cipher/example.pony +6 -4
- data/tracks/pony/exercises/atbash-cipher/test.pony +24 -38
- data/tracks/pony/exercises/pascals-triangle/test.pony +3 -3
- data/tracks/pony/exercises/rna-transcription/test.pony +4 -4
- data/tracks/pony/exercises/roman-numerals/example.pony +12 -12
- data/tracks/pony/exercises/roman-numerals/test.pony +18 -18
- data/tracks/purescript/config.json +15 -0
- data/tracks/purescript/exercises/etl/bower.json +17 -0
- data/tracks/purescript/exercises/etl/examples/src/Etl.purs +20 -0
- data/tracks/purescript/exercises/etl/src/Etl.purs +3 -0
- data/tracks/purescript/exercises/etl/test/Main.purs +99 -0
- data/tracks/purescript/exercises/isogram/bower.json +18 -0
- data/tracks/purescript/exercises/isogram/examples/src/Isogram.purs +22 -0
- data/tracks/purescript/exercises/isogram/src/Isogram.purs +4 -0
- data/tracks/purescript/exercises/isogram/test/Main.purs +44 -0
- data/tracks/ruby/README.md +10 -9
- data/tracks/ruby/exercises/anagram/example.tt +1 -1
- data/tracks/ruby/exercises/bowling/example.tt +1 -1
- data/tracks/ruby/exercises/hamming/example.tt +2 -2
- data/tracks/ruby/exercises/luhn/.meta/.version +1 -0
- data/tracks/ruby/exercises/luhn/example.rb +18 -23
- data/tracks/ruby/exercises/luhn/example.tt +18 -0
- data/tracks/ruby/exercises/luhn/luhn_test.rb +60 -27
- data/tracks/ruby/exercises/pig-latin/.meta/.version +1 -0
- data/tracks/ruby/exercises/pig-latin/example.rb +4 -0
- data/tracks/ruby/exercises/pig-latin/example.tt +21 -0
- data/tracks/ruby/exercises/pig-latin/pig_latin_test.rb +73 -24
- data/tracks/ruby/exercises/rna-transcription/example.tt +1 -1
- data/tracks/ruby/lib/anagram_cases.rb +1 -1
- data/tracks/ruby/lib/binary_cases.rb +3 -3
- data/tracks/ruby/lib/bowling_cases.rb +1 -1
- data/tracks/ruby/lib/hamming_cases.rb +1 -1
- data/tracks/ruby/lib/luhn_cases.rb +27 -0
- data/tracks/ruby/lib/pig_latin_cases.rb +20 -0
- data/tracks/ruby/lib/rna_transcription_cases.rb +1 -1
- metadata +25 -3
- data/tracks/go/.github/stale.yml +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 350291aab0beae040bfec2a9a85f1e4faceac732
|
|
4
|
+
data.tar.gz: 00ecf8962d3f79bfff2872e5909644b808f80b8d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b3ab8fa14033accfc8470e239b3319b0b62f5d0d7139887792cd4c6f0733c89c8493200ae93b2be108e4b9853a7e9be526f8bfcdd76cb93fd55fb7dd5d4f0810
|
|
7
|
+
data.tar.gz: b03ed5cb2599463588b8109f49a471fa744cd6447010d287c92f486b0b498419c067bb1599c7e66d503e04626e69e1c5e2cab83225740a97f11410b67ddcff04
|
data/README.md
CHANGED
|
@@ -93,6 +93,13 @@ gem build trackler.gemspec
|
|
|
93
93
|
gem install --local trackler-$VERSION.gem
|
|
94
94
|
gem push trackler-$VERSION.gem
|
|
95
95
|
```
|
|
96
|
+
## Users
|
|
97
|
+
|
|
98
|
+
Projects that make use of this gem:
|
|
99
|
+
|
|
100
|
+
* https://github.com/exercism/exercism.io
|
|
101
|
+
* https://github.com/exercism/x-api
|
|
102
|
+
* https://github.com/sandimetz/99bottles-polyglot
|
|
96
103
|
|
|
97
104
|
## License
|
|
98
105
|
|
data/common/CONTRIBUTING.md
CHANGED
|
@@ -41,7 +41,7 @@ themselves. There are other guides about contributing to other parts of the Exer
|
|
|
41
41
|
* [Pull Request Guidelines](#pull-request-guidelines)
|
|
42
42
|
* [Exercise Versioning](#exercise-versioning)
|
|
43
43
|
* [Anatomy of an Exercise](#anatomy-of-an-exercise)
|
|
44
|
-
* [config.json](#
|
|
44
|
+
* [Track configuration file (config.json)](#track-configuration-file)
|
|
45
45
|
* [Track-Level Linting With Configlet](#track-level-linting-with-configlet)
|
|
46
46
|
* [Git Basics](#git-basics)
|
|
47
47
|
* [Getting the Code](#getting-the-code)
|
|
@@ -668,10 +668,11 @@ that the exercise is coherent.
|
|
|
668
668
|
If you change the test suite, then make sure the reference solution is fixed
|
|
669
669
|
to pass the updated tests.
|
|
670
670
|
|
|
671
|
-
###
|
|
671
|
+
### Track configuration file
|
|
672
672
|
|
|
673
|
-
Each language track has a `config.json
|
|
673
|
+
Each language track has a configuration file called `config.json`.
|
|
674
674
|
|
|
675
|
+
Important keys are:
|
|
675
676
|
* `problems` - actively served via `exercism fetch`
|
|
676
677
|
* `deprecated` - implemented, but aren't served anymore
|
|
677
678
|
* `foregone` - will not be implemented in the track
|
|
@@ -690,9 +691,9 @@ is useful. Ignored directories don't get flagged as unimplemented problems.
|
|
|
690
691
|
A problem might be foregone for a number of reasons, typically because it's a
|
|
691
692
|
bad exercise for the language.
|
|
692
693
|
|
|
693
|
-
|
|
694
|
-
test filenames will match.
|
|
695
|
-
|
|
694
|
+
Optional keys:
|
|
695
|
+
* `test_pattern` - A (case sensitive) regex pattern that test filenames will match. It is used to determine which files will be visible on a problem's test-suite page on the exercism.io site. The default value used if this key is not present is `test` (note: this is case _insensitve_)
|
|
696
|
+
* `ignore_pattern` - A (case insensitive) regex pattern that will cause files matching it to not be served to the student by `exercism fetch`. The default value used if this key is not present is `example`
|
|
696
697
|
|
|
697
698
|
### Track-Level Linting With Configlet
|
|
698
699
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
example test
|
|
@@ -4,30 +4,30 @@ require_relative 'file_bundle'
|
|
|
4
4
|
module Trackler
|
|
5
5
|
# Implementation is a language-specific implementation of an exercise.
|
|
6
6
|
class Implementation
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Regexp.new("/\.meta/")
|
|
7
|
+
IGNORE_PATTERNS = [
|
|
8
|
+
"\/HINTS\.md$",
|
|
9
|
+
"\/\.$",
|
|
10
|
+
"/\.meta/"
|
|
12
11
|
]
|
|
13
12
|
|
|
14
|
-
attr_reader :
|
|
13
|
+
attr_reader :track, :problem
|
|
15
14
|
attr_writer :files
|
|
16
|
-
def initialize(
|
|
17
|
-
@
|
|
18
|
-
@repo = repo
|
|
15
|
+
def initialize(track, problem)
|
|
16
|
+
@track = track
|
|
19
17
|
@problem = problem
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def file_bundle
|
|
21
|
+
@file_bundle ||= FileBundle.new(implementation_dir, regexes_to_ignore)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def exists?
|
|
25
|
-
File.exist?(
|
|
25
|
+
File.exist?(implementation_dir)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def files
|
|
29
29
|
@files ||= Hash[file_bundle.paths.map {|path|
|
|
30
|
-
[path.relative_path_from(
|
|
30
|
+
[path.relative_path_from(implementation_dir).to_s, File.read(path)]
|
|
31
31
|
}].merge("README.md" => readme)
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -42,26 +42,28 @@ module Trackler
|
|
|
42
42
|
@readme ||= assemble_readme
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def exercise_dir
|
|
46
|
-
if File.exist?(track_dir.join('exercises'))
|
|
47
|
-
File.join('exercises', problem.slug)
|
|
48
|
-
else
|
|
49
|
-
problem.slug
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
45
|
def git_url
|
|
54
|
-
[
|
|
46
|
+
[track.repository, "tree/master", exercise_dir].join("/")
|
|
55
47
|
end
|
|
56
48
|
|
|
57
49
|
private
|
|
58
50
|
|
|
59
|
-
def
|
|
60
|
-
|
|
51
|
+
def regexes_to_ignore
|
|
52
|
+
(IGNORE_PATTERNS + [@track.ignore_pattern]).map do |pattern|
|
|
53
|
+
Regexp.new(pattern, Regexp::IGNORECASE)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def implementation_dir
|
|
58
|
+
@implementation_dir ||= track.dir.join(exercise_dir)
|
|
61
59
|
end
|
|
62
60
|
|
|
63
|
-
def
|
|
64
|
-
|
|
61
|
+
def exercise_dir
|
|
62
|
+
if File.exist?(track.dir.join('exercises'))
|
|
63
|
+
File.join('exercises', problem.slug)
|
|
64
|
+
else
|
|
65
|
+
problem.slug
|
|
66
|
+
end
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
def assemble_readme
|
|
@@ -85,8 +87,8 @@ module Trackler
|
|
|
85
87
|
def readme_body
|
|
86
88
|
[
|
|
87
89
|
problem.description,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
implementation_hints,
|
|
91
|
+
track.hints,
|
|
90
92
|
].reject(&:empty?).join("\n").strip
|
|
91
93
|
end
|
|
92
94
|
|
|
@@ -101,16 +103,8 @@ It's possible to submit an incomplete solution so you can see how others have co
|
|
|
101
103
|
README
|
|
102
104
|
end
|
|
103
105
|
|
|
104
|
-
def
|
|
105
|
-
|
|
106
|
-
unless File.exist?(track_hints_filename)
|
|
107
|
-
track_hints_filename = track_dir.join('SETUP.md')
|
|
108
|
-
end
|
|
109
|
-
read track_hints_filename
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def implementation_hint
|
|
113
|
-
read File.join(track_directory, 'HINTS.md')
|
|
106
|
+
def implementation_hints
|
|
107
|
+
read File.join(implementation_dir, 'HINTS.md')
|
|
114
108
|
end
|
|
115
109
|
|
|
116
110
|
def read(f)
|
|
@@ -25,7 +25,7 @@ module Trackler
|
|
|
25
25
|
|
|
26
26
|
def all
|
|
27
27
|
@all ||= slugs.map { |slug|
|
|
28
|
-
Implementation.new(track
|
|
28
|
+
Implementation.new(track, Problem.new(slug, root, track))
|
|
29
29
|
}
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -35,7 +35,7 @@ module Trackler
|
|
|
35
35
|
|
|
36
36
|
def implementation_map
|
|
37
37
|
hash = Hash.new { |_, k|
|
|
38
|
-
Implementation.new(track
|
|
38
|
+
Implementation.new(track, Problem.new(k, root, track))
|
|
39
39
|
}
|
|
40
40
|
all.each do |impl|
|
|
41
41
|
hash[impl.problem.slug] = impl
|
data/lib/trackler/track.rb
CHANGED
|
@@ -81,6 +81,10 @@ module Trackler
|
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
def ignore_pattern
|
|
85
|
+
config.fetch('ignore_pattern', 'example')
|
|
86
|
+
end
|
|
87
|
+
|
|
84
88
|
def docs(positional_image_path_which_is_deprecated = nil, image_path: nil)
|
|
85
89
|
if positional_image_path_which_is_deprecated
|
|
86
90
|
warn "DEPRECATION WARNING:\ntrack.docs: Positional argument is deprecated, please use keyword argument 'image_path:' instead\neg: track.docs(image_path: #{positional_image_path_which_is_deprecated.inspect})\n"
|
|
@@ -107,6 +111,18 @@ module Trackler
|
|
|
107
111
|
active_slugs + foregone_slugs + deprecated_slugs
|
|
108
112
|
end
|
|
109
113
|
|
|
114
|
+
def dir
|
|
115
|
+
root.join("tracks", id)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def hints
|
|
119
|
+
track_hints_filename = dir.join('exercises', 'TRACK_HINTS.md')
|
|
120
|
+
unless File.exist?(track_hints_filename)
|
|
121
|
+
track_hints_filename = dir.join('SETUP.md')
|
|
122
|
+
end
|
|
123
|
+
read track_hints_filename
|
|
124
|
+
end
|
|
125
|
+
|
|
110
126
|
private
|
|
111
127
|
|
|
112
128
|
# The slugs for the problems that are currently in the track.
|
|
@@ -139,10 +155,6 @@ module Trackler
|
|
|
139
155
|
formats.max_by { |format| formats.count(format) }
|
|
140
156
|
end
|
|
141
157
|
|
|
142
|
-
def dir
|
|
143
|
-
root.join("tracks", id)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
158
|
def config
|
|
147
159
|
@config ||= JSON.parse(File.read(config_filename))
|
|
148
160
|
end
|
|
@@ -187,5 +199,13 @@ module Trackler
|
|
|
187
199
|
def png_icon
|
|
188
200
|
@png_icon ||= Image.new(File.join(dir, "img/icon.png"))
|
|
189
201
|
end
|
|
202
|
+
|
|
203
|
+
def read(f)
|
|
204
|
+
if File.exist?(f)
|
|
205
|
+
File.read(f)
|
|
206
|
+
else
|
|
207
|
+
""
|
|
208
|
+
end
|
|
209
|
+
end
|
|
190
210
|
end
|
|
191
211
|
end
|
data/lib/trackler/version.rb
CHANGED
data/tracks/elixir/config.json
CHANGED
|
@@ -220,6 +220,15 @@
|
|
|
220
220
|
"topics": [
|
|
221
221
|
]
|
|
222
222
|
},
|
|
223
|
+
{
|
|
224
|
+
"slug": "tournament",
|
|
225
|
+
"difficulty": 4,
|
|
226
|
+
"topics": [
|
|
227
|
+
"string processing",
|
|
228
|
+
"sorting",
|
|
229
|
+
"formatting"
|
|
230
|
+
]
|
|
231
|
+
},
|
|
223
232
|
{
|
|
224
233
|
"slug": "list-ops",
|
|
225
234
|
"difficulty": 4,
|
|
@@ -385,6 +394,14 @@
|
|
|
385
394
|
"topics": [
|
|
386
395
|
]
|
|
387
396
|
},
|
|
397
|
+
{
|
|
398
|
+
"slug": "simple-cipher",
|
|
399
|
+
"difficulty": 3,
|
|
400
|
+
"topics": [
|
|
401
|
+
"string processing",
|
|
402
|
+
"encryption"
|
|
403
|
+
]
|
|
404
|
+
},
|
|
388
405
|
{
|
|
389
406
|
"slug": "bank-account",
|
|
390
407
|
"difficulty": 7,
|
|
@@ -9,57 +9,72 @@ defmodule BracketPushTest do
|
|
|
9
9
|
use ExUnit.Case
|
|
10
10
|
|
|
11
11
|
# @tag :pending
|
|
12
|
+
test "paired square brackets" do
|
|
13
|
+
assert BracketPush.check_brackets("[]")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@tag :pending
|
|
12
17
|
test "empty string" do
|
|
13
18
|
assert BracketPush.check_brackets("")
|
|
14
19
|
end
|
|
15
20
|
|
|
16
21
|
@tag :pending
|
|
17
|
-
test "
|
|
18
|
-
|
|
22
|
+
test "unpaired brackets" do
|
|
23
|
+
refute BracketPush.check_brackets("[[")
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
@tag :pending
|
|
22
|
-
test "
|
|
23
|
-
refute BracketPush.check_brackets("{
|
|
27
|
+
test "wrong ordered brackets" do
|
|
28
|
+
refute BracketPush.check_brackets("}{")
|
|
24
29
|
end
|
|
25
30
|
|
|
26
31
|
@tag :pending
|
|
27
|
-
test "
|
|
28
|
-
|
|
32
|
+
test "wrong closing bracket" do
|
|
33
|
+
refute BracketPush.check_brackets("{]")
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
@tag :pending
|
|
32
|
-
test "
|
|
33
|
-
|
|
37
|
+
test "paired with whitespace" do
|
|
38
|
+
assert BracketPush.check_brackets("{ }")
|
|
34
39
|
end
|
|
35
40
|
|
|
36
41
|
@tag :pending
|
|
37
|
-
test "nested brackets" do
|
|
38
|
-
assert BracketPush.check_brackets("{[
|
|
42
|
+
test "simple nested brackets" do
|
|
43
|
+
assert BracketPush.check_brackets("{[]}")
|
|
39
44
|
end
|
|
40
45
|
|
|
41
46
|
@tag :pending
|
|
42
|
-
test "
|
|
43
|
-
|
|
47
|
+
test "several paired brackets" do
|
|
48
|
+
assert BracketPush.check_brackets("{}[]")
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
@tag :pending
|
|
47
|
-
test "
|
|
52
|
+
test "paired and nested brackets" do
|
|
53
|
+
assert BracketPush.check_brackets("([{}({}[])])")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@tag :pending
|
|
57
|
+
test "unopened closing brackets" do
|
|
48
58
|
refute BracketPush.check_brackets("{[)][]}")
|
|
49
59
|
end
|
|
50
60
|
|
|
51
61
|
@tag :pending
|
|
52
|
-
test "
|
|
53
|
-
|
|
62
|
+
test "unpaired and nested brackets" do
|
|
63
|
+
refute BracketPush.check_brackets("([{])")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@tag :pending
|
|
67
|
+
test "paired and wrong nested brackets" do
|
|
68
|
+
refute BracketPush.check_brackets("[({]})")
|
|
54
69
|
end
|
|
55
70
|
|
|
56
71
|
@tag :pending
|
|
57
|
-
test "
|
|
58
|
-
assert BracketPush.check_brackets("
|
|
72
|
+
test "math expression" do
|
|
73
|
+
assert BracketPush.check_brackets("(((185 + 223.85) * 15) - 543)/2")
|
|
59
74
|
end
|
|
60
75
|
|
|
61
76
|
@tag :pending
|
|
62
|
-
test "
|
|
63
|
-
assert BracketPush.check_brackets("
|
|
77
|
+
test "complex latex expression" do
|
|
78
|
+
assert BracketPush.check_brackets("\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)")
|
|
64
79
|
end
|
|
65
80
|
end
|
|
@@ -10,61 +10,61 @@ defmodule SaddlePointsTest do
|
|
|
10
10
|
|
|
11
11
|
# @tag :pending
|
|
12
12
|
test "extract rows" do
|
|
13
|
-
rows =
|
|
13
|
+
rows = SaddlePoints.rows("1 2\n10 20")
|
|
14
14
|
assert rows == [[1, 2], [10, 20]]
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
@tag :pending
|
|
18
18
|
test "extract a row" do
|
|
19
|
-
rows =
|
|
19
|
+
rows = SaddlePoints.rows("9 7\n8 6")
|
|
20
20
|
assert Enum.at(rows, 0) == [9, 7]
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
@tag :pending
|
|
24
24
|
test "extract other row" do
|
|
25
|
-
rows =
|
|
25
|
+
rows = SaddlePoints.rows("9 8 7\n19 18 17")
|
|
26
26
|
assert Enum.at(rows, 1) == [19, 18, 17]
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
@tag :pending
|
|
30
30
|
test "extract other row again" do
|
|
31
|
-
rows =
|
|
31
|
+
rows = SaddlePoints.rows("1 4 9\n16 25 36")
|
|
32
32
|
assert Enum.at(rows, 1) == [16, 25, 36]
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
@tag :pending
|
|
36
36
|
test "extract a column" do
|
|
37
|
-
columns =
|
|
37
|
+
columns = SaddlePoints.columns("1 2 3\n4 5 6\n7 8 9\n8 7 6")
|
|
38
38
|
assert Enum.at(columns, 0) == [1, 4, 7, 8]
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
@tag :pending
|
|
42
42
|
test "extract another column" do
|
|
43
|
-
columns =
|
|
43
|
+
columns = SaddlePoints.columns("89 1903 3\n18 3 1\n9 4 800")
|
|
44
44
|
assert Enum.at(columns, 1) == [1903, 3, 4]
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
@tag :pending
|
|
48
48
|
test "no saddle point" do
|
|
49
|
-
saddle_points =
|
|
49
|
+
saddle_points = SaddlePoints.saddle_points("2 1\n1 2")
|
|
50
50
|
assert saddle_points == []
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
@tag :pending
|
|
54
54
|
test "a saddle point" do
|
|
55
|
-
saddle_points =
|
|
55
|
+
saddle_points = SaddlePoints.saddle_points("1 2\n3 4")
|
|
56
56
|
assert saddle_points == [{0, 1}]
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
@tag :pending
|
|
60
60
|
test "another saddle point" do
|
|
61
|
-
saddle_points =
|
|
61
|
+
saddle_points = SaddlePoints.saddle_points("18 3 39 19 91\n38 10 8 77 320\n3 4 8 6 7")
|
|
62
62
|
assert saddle_points == [{2, 2}]
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
@tag :pending
|
|
66
66
|
test "multiple saddle points" do
|
|
67
|
-
saddle_points =
|
|
67
|
+
saddle_points = SaddlePoints.saddle_points("4 5 4\n3 5 5\n1 5 4")
|
|
68
68
|
assert saddle_points == [{0, 1}, {1, 1}, {2, 1}]
|
|
69
69
|
end
|
|
70
70
|
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
defmodule SimpleCipher do
|
|
2
|
+
@alphabet "abcdefghijklmnopqrstuvwxyz" |> String.graphemes
|
|
3
|
+
@alphabet_size @alphabet |> length
|
|
4
|
+
|
|
5
|
+
for key_char <- @alphabet do
|
|
6
|
+
shifted_alphabet = Stream.cycle(@alphabet)
|
|
7
|
+
|> Stream.drop_while(&(&1 != key_char))
|
|
8
|
+
|> Enum.take(@alphabet_size)
|
|
9
|
+
|
|
10
|
+
for { plain, cipher } <- Enum.zip(@alphabet, shifted_alphabet) do
|
|
11
|
+
defp encode_char(unquote(plain), unquote(key_char)), do: unquote(cipher)
|
|
12
|
+
defp decode_char(unquote(cipher), unquote(key_char)), do: unquote(plain)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
defp encode_char(plain, _), do: plain
|
|
17
|
+
defp decode_char(cipher, _), do: cipher
|
|
18
|
+
|
|
19
|
+
defp encode_char({plain, key}), do: encode_char(plain, key)
|
|
20
|
+
defp decode_char({cipher, key}), do: decode_char(cipher, key)
|
|
21
|
+
|
|
22
|
+
@doc """
|
|
23
|
+
Given a `plaintext` and `key`, encode each character of the `plaintext` by
|
|
24
|
+
shifting it by the corresponding letter in the alphabet shifted by the number
|
|
25
|
+
of letters represented by the `key` character, repeating the `key` if it is
|
|
26
|
+
shorter than the `plaintext`.
|
|
27
|
+
|
|
28
|
+
For example, for the letter 'd', the alphabet is rotated to become:
|
|
29
|
+
|
|
30
|
+
defghijklmnopqrstuvwxyzabc
|
|
31
|
+
|
|
32
|
+
You would encode the `plaintext` by taking the current letter and mapping it
|
|
33
|
+
to the letter in the same position in this rotated alphabet.
|
|
34
|
+
|
|
35
|
+
abcdefghijklmnopqrstuvwxyz
|
|
36
|
+
defghijklmnopqrstuvwxyzabc
|
|
37
|
+
|
|
38
|
+
"a" becomes "d", "t" becomes "w", etc...
|
|
39
|
+
|
|
40
|
+
Each letter in the `plaintext` will be encoded with the alphabet of the `key`
|
|
41
|
+
character in the same position. If the `key` is shorter than the `plaintext`,
|
|
42
|
+
repeat the `key`.
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
|
|
46
|
+
plaintext = "testing"
|
|
47
|
+
key = "abc"
|
|
48
|
+
|
|
49
|
+
The key should repeat to become the same length as the text, becoming
|
|
50
|
+
"abcabca". If the key is longer than the text, only use as many letters of it
|
|
51
|
+
as are necessary.
|
|
52
|
+
"""
|
|
53
|
+
def encode(plaintext, key) do
|
|
54
|
+
convert_keystream(plaintext, key, &encode_char/1)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
@doc """
|
|
58
|
+
Given a `ciphertext` and `key`, decode each character of the `ciphertext` by
|
|
59
|
+
finding the corresponding letter in the alphabet shifted by the number of
|
|
60
|
+
letters represented by the `key` character, repeating the `key` if it is
|
|
61
|
+
shorter than the `ciphertext`.
|
|
62
|
+
|
|
63
|
+
The same rules for key length and shifted alphabets apply as in `encode/2`,
|
|
64
|
+
but you will go the opposite way, so "d" becomes "a", "w" becomes "t",
|
|
65
|
+
etc..., depending on how much you shift the alphabet.
|
|
66
|
+
"""
|
|
67
|
+
def decode(ciphertext, key) do
|
|
68
|
+
convert_keystream(ciphertext, key, &decode_char/1)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
defp convert_keystream(text, key, converter) do
|
|
72
|
+
keystream = key |> String.graphemes |> Stream.cycle
|
|
73
|
+
|
|
74
|
+
text
|
|
75
|
+
|> String.graphemes
|
|
76
|
+
|> Enum.zip(keystream)
|
|
77
|
+
|> Enum.map_join(converter)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
defmodule SimpleCipher do
|
|
2
|
+
@doc """
|
|
3
|
+
Given a `plaintext` and `key`, encode each character of the `plaintext` by
|
|
4
|
+
shifting it by the corresponding letter in the alphabet shifted by the number
|
|
5
|
+
of letters represented by the `key` character, repeating the `key` if it is
|
|
6
|
+
shorter than the `plaintext`.
|
|
7
|
+
|
|
8
|
+
For example, for the letter 'd', the alphabet is rotated to become:
|
|
9
|
+
|
|
10
|
+
defghijklmnopqrstuvwxyzabc
|
|
11
|
+
|
|
12
|
+
You would encode the `plaintext` by taking the current letter and mapping it
|
|
13
|
+
to the letter in the same position in this rotated alphabet.
|
|
14
|
+
|
|
15
|
+
abcdefghijklmnopqrstuvwxyz
|
|
16
|
+
defghijklmnopqrstuvwxyzabc
|
|
17
|
+
|
|
18
|
+
"a" becomes "d", "t" becomes "w", etc...
|
|
19
|
+
|
|
20
|
+
Each letter in the `plaintext` will be encoded with the alphabet of the `key`
|
|
21
|
+
character in the same position. If the `key` is shorter than the `plaintext`,
|
|
22
|
+
repeat the `key`.
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
|
|
26
|
+
plaintext = "testing"
|
|
27
|
+
key = "abc"
|
|
28
|
+
|
|
29
|
+
The key should repeat to become the same length as the text, becoming
|
|
30
|
+
"abcabca". If the key is longer than the text, only use as many letters of it
|
|
31
|
+
as are necessary.
|
|
32
|
+
"""
|
|
33
|
+
def encode(plaintext, key) do
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
@doc """
|
|
37
|
+
Given a `ciphertext` and `key`, decode each character of the `ciphertext` by
|
|
38
|
+
finding the corresponding letter in the alphabet shifted by the number of
|
|
39
|
+
letters represented by the `key` character, repeating the `key` if it is
|
|
40
|
+
shorter than the `ciphertext`.
|
|
41
|
+
|
|
42
|
+
The same rules for key length and shifted alphabets apply as in `encode/2`,
|
|
43
|
+
but you will go the opposite way, so "d" becomes "a", "w" becomes "t",
|
|
44
|
+
etc..., depending on how much you shift the alphabet.
|
|
45
|
+
"""
|
|
46
|
+
def decode(ciphertext, key) do
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|