Преглед изворни кода

Add new exercise Variable Length Quantity (#1348)

* Add new exercise Variable Length Quantity

* Fix test file

* Get rid of unchecked/unsafe operations warning

* Fix typo in config.json

* Add List imoprt to starter solution

* Break up some tests into multiple lines

* Throw an IllegalArgumentException in error cases.

* Add more topics

* Indent multi-line arguments
Jack Mustacato пре 8 година
родитељ
комит
4e499367ae

+ 15
- 0
config.json Прегледај датотеку

@@ -434,6 +434,21 @@
434 434
       "uuid": "c3e89c7c-3a8a-4ddc-b653-9b0ff9e1d7d8"
435 435
     },
436 436
     {
437
+      "core": false,
438
+      "difficulty": 4,
439
+      "slug": "variable-length-quantity",
440
+      "topics": [
441
+        "bitwise_operations",
442
+        "conditionals",
443
+        "exception_handling",
444
+        "lists",
445
+        "loops",
446
+        "transforming"
447
+      ],
448
+      "unlocked_by": "secret-handshake",
449
+      "uuid": "d8a2c7ba-2040-4cfe-ab15-f90b3b61dd89"
450
+    },
451
+    {
437 452
       "core": true,
438 453
       "difficulty": 5,
439 454
       "slug": "flatten-array",

+ 1
- 0
exercises/settings.gradle Прегледај датотеку

@@ -94,6 +94,7 @@ include 'trinary'
94 94
 include 'twelve-days'
95 95
 include 'two-bucket'
96 96
 include 'two-fer'
97
+include 'variable-length-quantity'
97 98
 include 'word-count'
98 99
 include 'word-search'
99 100
 include 'wordy'

+ 53
- 0
exercises/variable-length-quantity/.meta/src/reference/java/VariableLengthQuantity.java Прегледај датотеку

@@ -0,0 +1,53 @@
1
+import java.util.ArrayList;
2
+import java.util.Collections;
3
+import java.util.List;
4
+
5
+class VariableLengthQuantity {
6
+
7
+    private long sevenBitsMask = 0x7f;
8
+    private long eightBitsMask = 0x80;
9
+
10
+    List<String> encodeSingleNumber(long number) {
11
+        List<String> bytes = new ArrayList<String>();
12
+        bytes.add("0x" + Long.toHexString(number & sevenBitsMask));
13
+        number >>= 7;
14
+
15
+        while (number > 0) {
16
+            bytes.add("0x" + Long.toHexString(number & sevenBitsMask | eightBitsMask));
17
+            number >>= 7;
18
+        }
19
+
20
+        Collections.reverse(bytes);
21
+        return bytes;
22
+    }
23
+
24
+    List<String> encode(List<Long> numbers) {
25
+        List<String> bytes = new ArrayList<String>();
26
+        for (long number : numbers) {
27
+            bytes.addAll(encodeSingleNumber(number));
28
+        }
29
+
30
+        return bytes;
31
+    }
32
+
33
+    List<String> decode(List<Long> bytes) {
34
+        List<String> numbers = new ArrayList<String>();
35
+        int i = 0;
36
+        long number = 0;
37
+
38
+        for (long b : bytes) {
39
+            number <<= 7;
40
+            number += b & sevenBitsMask;
41
+
42
+            if ((b & eightBitsMask) == 0) {
43
+                numbers.add("0x" + Long.toHexString(number));
44
+                number = 0;
45
+                i++;
46
+            } else if (i == bytes.size() - 1) {
47
+                throw new IllegalArgumentException("Invalid variable-length quantity encoding");
48
+            }
49
+        }
50
+
51
+        return numbers;
52
+    }
53
+}

+ 1
- 0
exercises/variable-length-quantity/.meta/version Прегледај датотеку

@@ -0,0 +1 @@
1
+1.1.0

+ 50
- 0
exercises/variable-length-quantity/README.md Прегледај датотеку

@@ -0,0 +1,50 @@
1
+# Variable Length Quantity
2
+
3
+Implement variable length quantity encoding and decoding.
4
+
5
+The goal of this exercise is to implement [VLQ](https://en.wikipedia.org/wiki/Variable-length_quantity) encoding/decoding.
6
+
7
+In short, the goal of this encoding is to encode integer values in a way that would save bytes.
8
+Only the first 7 bits of each byte is significant (right-justified; sort of like an ASCII byte).
9
+So, if you have a 32-bit value, you have to unpack it into a series of 7-bit bytes.
10
+Of course, you will have a variable number of bytes depending upon your integer.
11
+To indicate which is the last byte of the series, you leave bit #7 clear.
12
+In all of the preceding bytes, you set bit #7.
13
+
14
+So, if an integer is between `0-127`, it can be represented as one byte.
15
+Although VLQ can deal with numbers of arbitrary sizes, for this exercise we will restrict ourselves to only numbers that fit in a 32-bit unsigned integer.
16
+Here are examples of integers as 32-bit values, and the variable length quantities that they translate to:
17
+
18
+```text
19
+ NUMBER        VARIABLE QUANTITY
20
+00000000              00
21
+00000040              40
22
+0000007F              7F
23
+00000080             81 00
24
+00002000             C0 00
25
+00003FFF             FF 7F
26
+00004000           81 80 00
27
+00100000           C0 80 00
28
+001FFFFF           FF FF 7F
29
+00200000          81 80 80 00
30
+08000000          C0 80 80 00
31
+0FFFFFFF          FF FF FF 7F
32
+```
33
+
34
+# Running the tests
35
+
36
+You can run all the tests for an exercise by entering
37
+
38
+```sh
39
+$ gradle test
40
+```
41
+
42
+in your terminal.
43
+
44
+## Source
45
+
46
+A poor Splice developer having to implement MIDI encoding/decoding. [https://splice.com](https://splice.com)
47
+
48
+## Submitting Incomplete Solutions
49
+
50
+It's possible to submit an incomplete solution so you can see how others have completed the exercise.

+ 18
- 0
exercises/variable-length-quantity/build.gradle Прегледај датотеку

@@ -0,0 +1,18 @@
1
+apply plugin: "java"
2
+apply plugin: "eclipse"
3
+apply plugin: "idea"
4
+
5
+repositories {
6
+    mavenCentral()
7
+}
8
+
9
+dependencies {
10
+    testCompile "junit:junit:4.12"
11
+}
12
+
13
+test {
14
+    testLogging {
15
+        exceptionFormat = 'full'
16
+        events = ["passed", "failed", "skipped"]
17
+    }
18
+}

+ 12
- 0
exercises/variable-length-quantity/src/main/java/VariableLengthQuantity.java Прегледај датотеку

@@ -0,0 +1,12 @@
1
+import java.util.List;
2
+
3
+class VariableLengthQuantity {
4
+
5
+    List<String> encode(List<Long> numbers) {
6
+        throw new UnsupportedOperationException("Delete this statement and write your own implementation.");
7
+    }
8
+
9
+    List<String> decode(List<Long> bytes) {
10
+        throw new UnsupportedOperationException("Delete this statement and write your own implementation.");
11
+    }
12
+}

+ 265
- 0
exercises/variable-length-quantity/src/test/java/VariableLengthQuantityTest.java Прегледај датотеку

@@ -0,0 +1,265 @@
1
+import org.junit.Before;
2
+import org.junit.Ignore;
3
+import org.junit.Rule;
4
+import org.junit.Test;
5
+import org.junit.rules.ExpectedException;
6
+
7
+import static org.junit.Assert.assertEquals;
8
+
9
+import java.util.Arrays;
10
+import java.util.List;
11
+
12
+public class VariableLengthQuantityTest {
13
+    @Rule
14
+    public ExpectedException expectedException = ExpectedException.none();
15
+
16
+    private VariableLengthQuantity variableLengthQuantity;
17
+
18
+    @Before
19
+    public void setup() {
20
+        variableLengthQuantity = new VariableLengthQuantity();
21
+    }
22
+
23
+    @Test
24
+    public void testZero() {
25
+        List<String> expected = Arrays.asList("0x0");
26
+        List<Long> numbers = Arrays.asList(0x0L);
27
+
28
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
29
+    }
30
+
31
+    @Ignore("Remove to run test")
32
+    @Test
33
+    public void testArbitrarySingleByte() {
34
+        List<String> expected = Arrays.asList("0x40");
35
+        List<Long> numbers = Arrays.asList(0x40L);
36
+
37
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
38
+    }
39
+
40
+    @Ignore("Remove to run test")
41
+    @Test
42
+    public void testLargestSingleByte() {
43
+        List<String> expected = Arrays.asList("0x7f");
44
+        List<Long> numbers = Arrays.asList(0x7fL);
45
+
46
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
47
+    }
48
+
49
+    @Ignore("Remove to run test")
50
+    @Test
51
+    public void testSmallestDoubleByte() {
52
+        List<String> expected = Arrays.asList("0x81", "0x0");
53
+        List<Long> numbers = Arrays.asList(0x80L);
54
+
55
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
56
+    }
57
+
58
+    @Ignore("Remove to run test")
59
+    @Test
60
+    public void testArbitraryDoubleByte() {
61
+        List<String> expected = Arrays.asList("0xc0", "0x0");
62
+        List<Long> numbers = Arrays.asList(0x2000L);
63
+
64
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
65
+    }
66
+
67
+    @Ignore("Remove to run test")
68
+    @Test
69
+    public void testLargestDoubleByte() {
70
+        List<String> expected = Arrays.asList("0xff", "0x7f");
71
+        List<Long> numbers = Arrays.asList(0x3fffL);
72
+
73
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
74
+    }
75
+
76
+    @Ignore("Remove to run test")
77
+    @Test
78
+    public void testSmallestTripleByte() {
79
+        List<String> expected = Arrays.asList("0x81", "0x80", "0x0");
80
+        List<Long> numbers = Arrays.asList(0x4000L);
81
+
82
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
83
+    }
84
+
85
+    @Ignore("Remove to run test")
86
+    @Test
87
+    public void testArbitraryTripleByte() {
88
+        List<String> expected = Arrays.asList("0xc0", "0x80", "0x0");
89
+        List<Long> numbers = Arrays.asList(0x100000L);
90
+
91
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
92
+    }
93
+
94
+    @Ignore("Remove to run test")
95
+    @Test
96
+    public void testLargestTripleByte() {
97
+       List<String> expected = Arrays.asList("0xff", "0xff", "0x7f");
98
+       List<Long> numbers = Arrays.asList(0x1fffffL);
99
+
100
+       assertEquals(expected, variableLengthQuantity.encode(numbers));
101
+    }
102
+
103
+    @Ignore("Remove to run test")
104
+    @Test
105
+    public void testSmallestQuadrupleByte() {
106
+        List<String> expected = Arrays.asList("0x81", "0x80", "0x80", "0x0");
107
+        List<Long> numbers = Arrays.asList(0x200000L);
108
+
109
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
110
+    }
111
+
112
+    @Ignore("Remove to run test")
113
+    @Test
114
+    public void testArbitraryQuadrupleByte() {
115
+        List<String> expected = Arrays.asList("0xc0", "0x80", "0x80", "0x0");
116
+        List<Long> numbers = Arrays.asList(0x8000000L);
117
+
118
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
119
+    }
120
+
121
+    @Ignore("Remove to run test")
122
+    @Test
123
+    public void testLargestQuadrupleByte() {
124
+        List<String> expected = Arrays.asList("0xff", "0xff", "0xff", "0x7f");
125
+        List<Long> numbers = Arrays.asList(0xfffffffL);
126
+
127
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
128
+    }
129
+
130
+    @Ignore("Remove to run test")
131
+    @Test
132
+    public void testSmallestQuintupleByte() {
133
+        List<String> expected = Arrays.asList("0x81", "0x80", "0x80", "0x80", "0x0");
134
+        List<Long> numbers = Arrays.asList(0x10000000L);
135
+
136
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
137
+    }
138
+
139
+    @Ignore("Remove to run test")
140
+    @Test
141
+    public void testArbitraryQuintupleByte() {
142
+        List<String> expected = Arrays.asList("0x8f", "0xf8", "0x80", "0x80", "0x0");
143
+        List<Long> numbers = Arrays.asList(0xff000000L);
144
+
145
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
146
+    }
147
+
148
+    @Ignore("Remove to run test")
149
+    @Test
150
+    public void testMaximum32BitIntegerInput() {
151
+        List<String> expected = Arrays.asList("0x8f", "0xff", "0xff", "0xff", "0x7f");
152
+        List<Long> numbers = Arrays.asList(0xffffffffL);
153
+
154
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
155
+    }
156
+
157
+    @Ignore("Remove to run test")
158
+    @Test
159
+    public void testTwoSingleByteValues() {
160
+        List<String> expected = Arrays.asList("0x40", "0x7f");
161
+        List<Long> numbers = Arrays.asList(0x40L, 0x7fL);
162
+
163
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
164
+    }
165
+
166
+    @Ignore("Remove to run test")
167
+    @Test
168
+    public void testTwoMultiByteValues() {
169
+        List<String> expected = Arrays.asList("0x81", "0x80", "0x0", "0xc8", "0xe8", "0x56");
170
+        List<Long> numbers = Arrays.asList(0x4000L, 0x123456L);
171
+
172
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
173
+    }
174
+
175
+    @Ignore("Remove to run test")
176
+    @Test
177
+    public void testManyMultiByteValues() {
178
+        List<String> expected = Arrays.asList("0xc0", "0x0", "0xc8", "0xe8",
179
+                                              "0x56", "0xff", "0xff", "0xff",
180
+                                              "0x7f", "0x0", "0xff", "0x7f",
181
+                                              "0x81", "0x80", "0x0");
182
+        List<Long> numbers = Arrays.asList(0x2000L, 0x123456L, 0xfffffffL,
183
+                                           0x0L, 0x3fffL, 0x4000L);
184
+
185
+        assertEquals(expected, variableLengthQuantity.encode(numbers));
186
+    }
187
+
188
+    @Ignore("Remove to run test")
189
+    @Test
190
+    public void testDecodeOneByte() {
191
+        List<String> expected = Arrays.asList("0x7f");
192
+        List<Long> bytes = Arrays.asList(0x7fL);
193
+
194
+        assertEquals(expected, variableLengthQuantity.decode(bytes));
195
+    }
196
+
197
+    @Ignore("Remove to run test")
198
+    @Test
199
+    public void testDecodeTwoBytes() {
200
+        List<String> expected = Arrays.asList("0x2000");
201
+        List<Long> bytes = Arrays.asList(0xc0L, 0x0L);
202
+
203
+        assertEquals(expected, variableLengthQuantity.decode(bytes));
204
+    }
205
+
206
+    @Ignore("Remove to run test")
207
+    @Test
208
+    public void testDecodeThreeBytes() {
209
+        List<String> expected = Arrays.asList("0x1fffff");
210
+        List<Long> bytes = Arrays.asList(0xffL, 0xffL, 0x7fL);
211
+
212
+        assertEquals(expected, variableLengthQuantity.decode(bytes));
213
+    }
214
+
215
+    @Ignore("Remove to run test")
216
+    @Test
217
+    public void testDecodeFourBytes() {
218
+        List<String> expected = Arrays.asList("0x200000");
219
+        List<Long> bytes = Arrays.asList(0x81L, 0x80L, 0x80L, 0x0L);
220
+
221
+        assertEquals(expected, variableLengthQuantity.decode(bytes));
222
+    }
223
+
224
+    @Ignore("Remove to run test")
225
+    @Test
226
+    public void testDecodeMaximum32BitInteger() {
227
+        List<String> expected = Arrays.asList("0xffffffff");
228
+        List<Long> bytes = Arrays.asList(0x8fL, 0xffL, 0xffL, 0xffL, 0x7fL);
229
+
230
+        assertEquals(expected, variableLengthQuantity.decode(bytes));
231
+    }
232
+
233
+    @Ignore("Remove to run test")
234
+    @Test
235
+    public void testCannotDecodeIncompleteSequence() {
236
+        List<Long> bytes = Arrays.asList(0xffL);
237
+        expectedException.expect(IllegalArgumentException.class);
238
+        expectedException.expectMessage("Invalid variable-length quantity encoding");
239
+
240
+        variableLengthQuantity.decode(bytes);
241
+    }
242
+
243
+    @Ignore("Remove to run test")
244
+    @Test
245
+    public void testCannotDecodeIncompleteSequenceEvenIfValueIsZero() {
246
+        List<Long> bytes = Arrays.asList(0x80L);
247
+        expectedException.expect(IllegalArgumentException.class);
248
+        expectedException.expectMessage("Invalid variable-length quantity encoding");
249
+
250
+        variableLengthQuantity.decode(bytes);
251
+    }
252
+
253
+    @Ignore("Remove to run test")
254
+    @Test
255
+    public void testDecodeMultipleBytes() {
256
+        List<String> expected = Arrays.asList("0x2000", "0x123456",
257
+                                              "0xfffffff", "0x0", "0x3fff",
258
+                                              "0x4000");
259
+        List<Long> bytes = Arrays.asList(0xc0L, 0x0L, 0xc8L, 0xe8L, 0x56L,
260
+                                         0xffL, 0xffL, 0xffL, 0x7fL, 0x0L,
261
+                                         0xffL, 0x7fL, 0x81L, 0x80L, 0x0L);
262
+
263
+        assertEquals(expected, variableLengthQuantity.decode(bytes));
264
+    }
265
+}