trackler 2.0.6.34 → 2.0.6.35
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/common/exercises/change/canonical-data.json +6 -0
- data/common/exercises/luhn/canonical-data.json +35 -0
- data/common/exercises/rotational-cipher/cannonical-data.json +70 -0
- data/common/exercises/rotational-cipher/description.md +30 -0
- data/common/exercises/rotational-cipher/metadata.yml +4 -0
- data/common/exercises/run-length-encoding/description.md +7 -1
- data/lib/trackler/version.rb +1 -1
- data/tracks/elixir/exercises/run-length-encoding/example.exs +15 -4
- data/tracks/elixir/exercises/run-length-encoding/rle.exs +2 -2
- data/tracks/elixir/exercises/run-length-encoding/rle_test.exs +28 -11
- data/tracks/go/exercises/bob/bob_test.go +4 -1
- data/tracks/go/exercises/bracket-push/bracket_push_test.go +5 -2
- data/tracks/go/exercises/circular-buffer/circular_buffer_test.go +6 -6
- data/tracks/go/exercises/clock/clock_test.go +4 -1
- data/tracks/go/exercises/connect/connect_test.go +4 -1
- data/tracks/go/exercises/crypto-square/crypto_square_test.go +5 -2
- data/tracks/go/exercises/luhn/luhn_test.go +1 -1
- data/tracks/javascript/exercises/leap/leap.js +1 -1
- data/tracks/julia/.travis.yml +0 -2
- data/tracks/julia/config.json +41 -0
- data/tracks/julia/exercises/custom-set/HINTS.md +1 -0
- data/tracks/julia/exercises/custom-set/custom-set.jl +0 -0
- data/tracks/julia/exercises/custom-set/example.jl +51 -0
- data/tracks/julia/exercises/custom-set/runtests.jl +200 -0
- data/tracks/julia/exercises/isogram/example.jl +11 -0
- data/tracks/julia/exercises/isogram/isogram.jl +3 -0
- data/tracks/julia/exercises/isogram/runtests.jl +35 -0
- data/tracks/julia/exercises/luhn/example.jl +16 -0
- data/tracks/julia/exercises/luhn/luhn.jl +0 -0
- data/tracks/julia/exercises/luhn/runtests.jl +31 -0
- data/tracks/julia/exercises/nucleotide-count/example.jl +8 -0
- data/tracks/julia/exercises/nucleotide-count/nucleotide-count.jl +3 -0
- data/tracks/julia/exercises/nucleotide-count/runtests.jl +19 -0
- data/tracks/kotlin/.travis.yml +9 -1
- data/tracks/kotlin/README.md +117 -11
- data/tracks/kotlin/bin/journey-test.sh +279 -0
- data/tracks/kotlin/docs/INSTALLATION.md +193 -97
- data/tracks/kotlin/docs/TESTS.md +72 -137
- data/tracks/kotlin/exercises/hello-world/GETTING_STARTED.md +50 -0
- data/tracks/kotlin/exercises/hello-world/TUTORIAL.md +693 -0
- data/tracks/kotlin/exercises/hello-world/src/example/kotlin/HelloWorld.kt +2 -30
- data/tracks/kotlin/exercises/hello-world/src/main/kotlin/HelloWorld.kt +2 -30
- data/tracks/kotlin/exercises/hello-world/src/test/kotlin/HelloWorldTest.kt +11 -19
- data/tracks/objective-c/circle.yml +1 -1
- data/tracks/perl6/exercises/atbash-cipher/{cipher.t → atbash-cipher.t} +0 -0
- data/tracks/perl6/exercises/linked-list/linked-list.t +0 -0
- data/tracks/perl6/exercises/phone-number/{phone.t → phone-number.t} +0 -0
- data/tracks/perl6/exercises/rna-transcription/{rna_transcription.t → rna-transcription.t} +0 -0
- data/tracks/perl6/exercises/robot-name/{robot.t → robot-name.t} +0 -0
- data/tracks/perl6/exercises/scrabble-score/{scrabble_score.t → scrabble-score.t} +0 -0
- data/tracks/perl6/exercises/word-count/{word_count.t → word-count.t} +0 -0
- data/tracks/pony/config.json +9 -0
- data/tracks/pony/exercises/pascals-triangle/example.pony +18 -0
- data/tracks/pony/exercises/pascals-triangle/test.pony +31 -0
- data/tracks/r/config.json +20 -0
- data/tracks/r/exercises/grains/example.R +16 -0
- data/tracks/r/exercises/grains/grains.R +7 -0
- data/tracks/r/exercises/grains/test_grains.R +49 -0
- data/tracks/r/exercises/phone-number/example.R +30 -0
- data/tracks/r/exercises/phone-number/phone-number.R +3 -0
- data/tracks/r/exercises/phone-number/test_phone-number.R +44 -0
- data/tracks/r/exercises/secret-handshake/example.R +26 -0
- data/tracks/r/exercises/secret-handshake/secret-handshake.R +3 -0
- data/tracks/r/exercises/secret-handshake/test_secret-handshake.R +52 -0
- data/tracks/r/exercises/sieve/example.R +16 -0
- data/tracks/r/exercises/sieve/sieve.R +3 -0
- data/tracks/r/exercises/sieve/test_sieve.R +37 -0
- data/tracks/rust/config.json +11 -0
- data/tracks/rust/exercises/rotational-cipher/Cargo.lock +4 -0
- data/tracks/rust/exercises/rotational-cipher/Cargo.toml +4 -0
- data/tracks/rust/exercises/rotational-cipher/example.rs +16 -0
- data/tracks/rust/exercises/rotational-cipher/tests/rotational-cipher.rs +61 -0
- data/tracks/swift/circle.yml +6 -0
- metadata +46 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b60180d66cb803d7af0b313725e9f050d9dc61b0
|
4
|
+
data.tar.gz: cba72b210897ddba5ce87dc79ca7541e9615ab43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cdaf21409803ea63c534daeca46434a8463f75b65a8e4a3908b924598786f69e90e4c7335b344ba48a60098215eac3c11ec510820e3f36081e1c9baddd23a8b7
|
7
|
+
data.tar.gz: a0b9a0e0ce5411a07b0a5e3221fcebe7da097c20cb83843fb3252d4c9d72f6a44d8861c7925eaf88738fcaff3011cf7e0141c82c38d7a7b2d0b5e45b1cc96bcd
|
@@ -48,6 +48,12 @@
|
|
48
48
|
"target": 3,
|
49
49
|
"expected": -1
|
50
50
|
},
|
51
|
+
{
|
52
|
+
"description": "error if no combination can add up to target",
|
53
|
+
"coins": [5, 10],
|
54
|
+
"target": 94,
|
55
|
+
"expected": -1
|
56
|
+
},
|
51
57
|
{
|
52
58
|
"description": "cannot find negative change values",
|
53
59
|
"coins": [1, 2, 5],
|
@@ -10,6 +10,11 @@
|
|
10
10
|
"input": "0",
|
11
11
|
"expected": false
|
12
12
|
},
|
13
|
+
{
|
14
|
+
"description": "simple valid sin",
|
15
|
+
"input": " 5 9 ",
|
16
|
+
"expected": true
|
17
|
+
},
|
13
18
|
{
|
14
19
|
"description": "valid Canadian SIN",
|
15
20
|
"input": "046 454 286",
|
@@ -29,6 +34,36 @@
|
|
29
34
|
"description": "valid strings with a non-digit added become invalid",
|
30
35
|
"input": "046a 454 286",
|
31
36
|
"expected": false
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"description": "punctuation is not allowed",
|
40
|
+
"input": "055-444-285",
|
41
|
+
"expected": false
|
42
|
+
},
|
43
|
+
{
|
44
|
+
"description": "symbols are not allowed",
|
45
|
+
"input": "055£ 444$ 285",
|
46
|
+
"expected": false
|
47
|
+
},
|
48
|
+
{
|
49
|
+
"description": "single zero with space is invalid",
|
50
|
+
"input": " 0",
|
51
|
+
"expected": false
|
52
|
+
},
|
53
|
+
{
|
54
|
+
"description": "lots of zeros are valid",
|
55
|
+
"input": " 00000",
|
56
|
+
"expected": true
|
57
|
+
},
|
58
|
+
{
|
59
|
+
"description": "another valid sin",
|
60
|
+
"input": "055 444 285",
|
61
|
+
"expected": true
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"description": "nine doubled is nine",
|
65
|
+
"input": "091",
|
66
|
+
"expected": true
|
32
67
|
}
|
33
68
|
]
|
34
69
|
}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
{
|
2
|
+
"#": [
|
3
|
+
"The tests are a series of rotation tests: "
|
4
|
+
],
|
5
|
+
"rotate": {
|
6
|
+
"description": ["Test rotation from English to ROTn"],
|
7
|
+
"cases": [
|
8
|
+
{
|
9
|
+
"description": "rotate a by 1",
|
10
|
+
"text": "a",
|
11
|
+
"shift_key": 1,
|
12
|
+
"expected": "b"
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"description": "rotate a by 26, same output as input",
|
16
|
+
"text": "a",
|
17
|
+
"shift_key": 26,
|
18
|
+
"expected": "a"
|
19
|
+
},
|
20
|
+
{
|
21
|
+
"description": "rotate a by 0, same output as input",
|
22
|
+
"text": "a",
|
23
|
+
"shift_key": 26,
|
24
|
+
"expected": "a"
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"description": "rotate m by 13",
|
28
|
+
"text": "m",
|
29
|
+
"shift_key": 13,
|
30
|
+
"expected": "z"
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"description": "rotate n by 13 with wrap around alphabet",
|
34
|
+
"text": "n",
|
35
|
+
"shift_key": 13,
|
36
|
+
"expected": "a"
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"description": "rotate capital letters",
|
40
|
+
"text": "OMG",
|
41
|
+
"shift_key": 5,
|
42
|
+
"expected": "TRL"
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"description": "rotate spaces",
|
46
|
+
"text": "O M G",
|
47
|
+
"shift_key": 5,
|
48
|
+
"expected": "T R L"
|
49
|
+
},
|
50
|
+
{
|
51
|
+
"description": "rotate numbers",
|
52
|
+
"text": "Testing 1 2 3 testing",
|
53
|
+
"shift_key": 4,
|
54
|
+
"expected": "Xiwxmrk 1 2 3 xiwxmrk"
|
55
|
+
},
|
56
|
+
{
|
57
|
+
"description": "rotate punctuation",
|
58
|
+
"text": "Let's eat, Grandma!",
|
59
|
+
"shift_key": 21,
|
60
|
+
"expected": "Gzo'n zvo, Bmviyhv!"
|
61
|
+
},
|
62
|
+
{
|
63
|
+
"description": "rotate all letters",
|
64
|
+
"text": "The quick brown fox jumps over the lazy dog.",
|
65
|
+
"shift_key": 13,
|
66
|
+
"expected": "Gur dhvpx oebja sbk whzcf bire gur ynml qbt."
|
67
|
+
}
|
68
|
+
]
|
69
|
+
}
|
70
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Rotational Cipher
|
2
|
+
|
3
|
+
Create an implementation of the rotational cipher, also sometimes called the Caesar cipher.
|
4
|
+
|
5
|
+
The Caesar cipher is a simple shift cipher that relies on
|
6
|
+
transposing all the letters in the alphabet using an integer key
|
7
|
+
between `0` and `26`. Using a key of `0` or `26` will always yield
|
8
|
+
the same output due to modular arithmetic. The letter is shifted
|
9
|
+
for as many values as the value of the key.
|
10
|
+
|
11
|
+
The general notation for rotational ciphers is `ROT + <key>`.
|
12
|
+
The most commonly used rotational cipher is `ROT13`.
|
13
|
+
|
14
|
+
A `ROT13` on the Latin alphabet would be as follows:
|
15
|
+
|
16
|
+
```plain
|
17
|
+
Plain: abcdefghijklmnopqrstuvwxyz
|
18
|
+
Cipher: nopqrstuvwxyzabcdefghijklm
|
19
|
+
```
|
20
|
+
|
21
|
+
It is stronger than the Atbash cipher because it has 27 possible keys, and 25 usable keys.
|
22
|
+
|
23
|
+
Ciphertext is written out in the same formatting as the input including spaces and punctuation.
|
24
|
+
|
25
|
+
## Examples
|
26
|
+
- ROT5 `omg` gives `trl`
|
27
|
+
- ROT0 `c` gives `c`
|
28
|
+
- ROT26 `Cool` gives `Cool`
|
29
|
+
- ROT13 `The quick brown fox jumps over the lazy dog.` gives `Gur dhvpx oebja sbk whzcf bire gur ynml qbt.`
|
30
|
+
- ROT13 `Gur dhvpx oebja sbk whzcf bire gur ynml qbt.` gives `The quick brown fox jumps over the lazy dog.`
|
@@ -14,5 +14,11 @@ the compressed data, which makes it a lossless data compression.
|
|
14
14
|
"AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE"
|
15
15
|
```
|
16
16
|
|
17
|
+
If the string contains any whitespace, it should be passed through unchanged:
|
18
|
+
|
19
|
+
```
|
20
|
+
"aabc dddef" -> "2abc 3def"
|
21
|
+
```
|
22
|
+
|
17
23
|
For simplicity, you can assume that the unencoded string will only contain
|
18
|
-
the letters A through Z.
|
24
|
+
the letters A through Z (either lower or uppercase) and whitespace.
|
data/lib/trackler/version.rb
CHANGED
@@ -2,13 +2,24 @@ defmodule RunLengthEncoder do
|
|
2
2
|
|
3
3
|
@spec encode(String.t) :: String.t
|
4
4
|
def encode(string) do
|
5
|
-
Regex.scan(~r/([
|
6
|
-
|> Enum.map_join(fn([run, c]) ->
|
5
|
+
Regex.scan(~r/([a-zA-Z ])\1*/, string)
|
6
|
+
|> Enum.map_join(fn([run, c]) ->
|
7
|
+
if String.match?(run, ~r/\s+/) do
|
8
|
+
run
|
9
|
+
else
|
10
|
+
times = String.length(run)
|
11
|
+
number = if times == 1 do "" else times end
|
12
|
+
"#{number}#{c}"
|
13
|
+
end
|
14
|
+
end)
|
7
15
|
end
|
8
16
|
|
9
17
|
@spec decode(String.t) :: String.t
|
10
18
|
def decode(string) do
|
11
|
-
Regex.scan(~r/(\d
|
12
|
-
|> Enum.map_join(fn [_,n,c] ->
|
19
|
+
Regex.scan(~r/(\d*)(.)/, string)
|
20
|
+
|> Enum.map_join(fn [_,n,c] ->
|
21
|
+
times = if n == "" do 1 else String.to_integer(n) end
|
22
|
+
String.duplicate(c, times)
|
23
|
+
end)
|
13
24
|
end
|
14
25
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
defmodule RunLengthEncoder do
|
2
2
|
@doc """
|
3
3
|
Generates a string where consecutive elements are represented as a data value and count.
|
4
|
-
"
|
4
|
+
"AABBBCCCC" => "2A3B4C"
|
5
5
|
For this example, assume all input are strings, that are all uppercase letters.
|
6
6
|
It should also be able to reconstruct the data into its original form.
|
7
|
-
"
|
7
|
+
"2A3B4C" => "AABBBCCCC"
|
8
8
|
"""
|
9
9
|
@spec encode(String.t) :: String.t
|
10
10
|
def encode(string) do
|
@@ -8,32 +8,49 @@ ExUnit.configure exclude: :pending, trace: true
|
|
8
8
|
defmodule RunLengthEncoderTest do
|
9
9
|
use ExUnit.Case
|
10
10
|
|
11
|
-
test "empty string
|
11
|
+
test "encode empty string" do
|
12
12
|
assert RunLengthEncoder.encode("") === ""
|
13
13
|
end
|
14
14
|
|
15
15
|
@tag :pending
|
16
|
-
test "
|
17
|
-
assert RunLengthEncoder.encode("
|
16
|
+
test "encode single characters only" do
|
17
|
+
assert RunLengthEncoder.encode("XYZ") === "XYZ"
|
18
18
|
end
|
19
19
|
|
20
20
|
@tag :pending
|
21
|
-
test "
|
22
|
-
assert RunLengthEncoder.
|
21
|
+
test "decode empty string" do
|
22
|
+
assert RunLengthEncoder.decode("") === ""
|
23
23
|
end
|
24
24
|
|
25
25
|
@tag :pending
|
26
|
-
test "
|
27
|
-
assert RunLengthEncoder.
|
26
|
+
test "decode single characters only" do
|
27
|
+
assert RunLengthEncoder.decode("XYZ") === "XYZ"
|
28
28
|
end
|
29
29
|
|
30
30
|
@tag :pending
|
31
|
-
test "
|
32
|
-
assert RunLengthEncoder.
|
31
|
+
test "encode simple" do
|
32
|
+
assert RunLengthEncoder.encode("AABBBCCCC") == "2A3B4C"
|
33
33
|
end
|
34
34
|
|
35
35
|
@tag :pending
|
36
|
-
test "
|
37
|
-
assert RunLengthEncoder.decode("
|
36
|
+
test "decode simple" do
|
37
|
+
assert RunLengthEncoder.decode("2A3B4C") == "AABBBCCCC"
|
38
|
+
end
|
39
|
+
|
40
|
+
@tag :pending
|
41
|
+
test "encode with single values" do
|
42
|
+
assert RunLengthEncoder.encode("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB") === "12WB12W3B24WB"
|
43
|
+
end
|
44
|
+
|
45
|
+
@tag :pending
|
46
|
+
test "decode with single values" do
|
47
|
+
assert RunLengthEncoder.decode("12WB12W3B24WB") === "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB"
|
48
|
+
end
|
49
|
+
|
50
|
+
@tag :pending
|
51
|
+
test "decode(encode(...)) combination" do
|
52
|
+
original = "zzz ZZ zZ"
|
53
|
+
encoded = RunLengthEncoder.encode(original)
|
54
|
+
assert RunLengthEncoder.decode(encoded) === original
|
38
55
|
end
|
39
56
|
end
|
@@ -4,10 +4,13 @@ import "testing"
|
|
4
4
|
|
5
5
|
const targetTestVersion = 2
|
6
6
|
|
7
|
-
func
|
7
|
+
func TestTestVersion(t *testing.T) {
|
8
8
|
if testVersion != targetTestVersion {
|
9
9
|
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
10
10
|
}
|
11
|
+
}
|
12
|
+
|
13
|
+
func TestHeyBob(t *testing.T) {
|
11
14
|
for _, tt := range testCases {
|
12
15
|
actual := Hey(tt.input)
|
13
16
|
if actual != tt.expected {
|
@@ -48,10 +48,13 @@ var testCases = []struct {
|
|
48
48
|
},
|
49
49
|
}
|
50
50
|
|
51
|
-
func
|
51
|
+
func TestTestVersion(t *testing.T) {
|
52
52
|
if testVersion != targetTestVersion {
|
53
|
-
t.Fatalf("Found testVersion = %v, want %v
|
53
|
+
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
54
54
|
}
|
55
|
+
}
|
56
|
+
|
57
|
+
func TestBracket(t *testing.T) {
|
55
58
|
for _, tt := range testCases {
|
56
59
|
actual, err := Bracket(tt.input)
|
57
60
|
// We don't expect errors for any of the test cases
|
@@ -21,12 +21,6 @@ import (
|
|
21
21
|
|
22
22
|
const targetTestVersion = 4
|
23
23
|
|
24
|
-
func TestTestVersion(t *testing.T) {
|
25
|
-
if testVersion != targetTestVersion {
|
26
|
-
t.Errorf("Found testVersion = %v, want %v.", testVersion, targetTestVersion)
|
27
|
-
}
|
28
|
-
}
|
29
|
-
|
30
24
|
// Here is one way you can have a test case verify that the expected
|
31
25
|
// interfaces are implemented.
|
32
26
|
|
@@ -94,6 +88,12 @@ func (tb testBuffer) overwrite(c byte) {
|
|
94
88
|
|
95
89
|
// tests. separate functions so log will have descriptive test name.
|
96
90
|
|
91
|
+
func TestTestVersion(t *testing.T) {
|
92
|
+
if testVersion != targetTestVersion {
|
93
|
+
t.Fatalf("Found testVersion = %v, want %v.", testVersion, targetTestVersion)
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
97
|
func TestReadEmptyBuffer(t *testing.T) {
|
98
98
|
tb := nb(1, t)
|
99
99
|
tb.readFail()
|
@@ -29,10 +29,13 @@ import (
|
|
29
29
|
|
30
30
|
const targetTestVersion = 4
|
31
31
|
|
32
|
-
func
|
32
|
+
func TestTestVersion(t *testing.T) {
|
33
33
|
if testVersion != targetTestVersion {
|
34
34
|
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
35
35
|
}
|
36
|
+
}
|
37
|
+
|
38
|
+
func TestCreateClock(t *testing.T) {
|
36
39
|
for _, n := range timeTests {
|
37
40
|
if got := New(n.h, n.m); got.String() != n.want {
|
38
41
|
t.Fatalf("New(%d, %d) = %q, want %q", n.h, n.m, got, n.want)
|
@@ -17,10 +17,13 @@ func prepare(lines []string) []string {
|
|
17
17
|
|
18
18
|
const targetTestVersion = 3
|
19
19
|
|
20
|
-
func
|
20
|
+
func TestTestVersion(t *testing.T) {
|
21
21
|
if testVersion != targetTestVersion {
|
22
22
|
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
23
23
|
}
|
24
|
+
}
|
25
|
+
|
26
|
+
func TestResultOf(t *testing.T) {
|
24
27
|
for _, tt := range testCases {
|
25
28
|
actual, err := ResultOf(prepare(tt.board))
|
26
29
|
// We don't expect errors for any of the test cases
|
@@ -86,10 +86,13 @@ var tests = []struct {
|
|
86
86
|
},
|
87
87
|
}
|
88
88
|
|
89
|
-
func
|
89
|
+
func TestTestVersion(t *testing.T) {
|
90
90
|
if testVersion != targetTestVersion {
|
91
|
-
t.
|
91
|
+
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
92
92
|
}
|
93
|
+
}
|
94
|
+
|
95
|
+
func TestEncode(t *testing.T) {
|
93
96
|
for _, test := range tests {
|
94
97
|
if ct := Encode(test.pt); ct != test.ct {
|
95
98
|
t.Fatalf(`Encode(%q):
|
@@ -26,7 +26,7 @@ func TestTestVersion(t *testing.T) {
|
|
26
26
|
func TestValid(t *testing.T) {
|
27
27
|
for _, test := range testCases {
|
28
28
|
if ok := Valid(test.input); ok != test.ok {
|
29
|
-
t.Fatalf("Valid(%s): %s\n\t Expected: %t\n\t Got: %t", test.input, test.description, ok,
|
29
|
+
t.Fatalf("Valid(%s): %s\n\t Expected: %t\n\t Got: %t", test.input, test.description, test.ok, ok)
|
30
30
|
}
|
31
31
|
}
|
32
32
|
}
|